Mercurial > hg > monetdb-java
view tests/JDBC_API_Tester.java @ 925:4dfdb62e3e9d
Extend JDBC_API_Tester program with optional second startup argument '-skipMALoutput'.
This allows the JDBC_API_Tester program to be called to skip the comparing the MAL output of tests which return MAL output such as PLAN, EXPLAIN and TRACE statements.
It is intended for internal development use only.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 08 Aug 2024 15:26:28 +0200 (8 months ago) |
parents | 3b29fb3f537a |
children | d311affc65f0 |
line wrap: on
line source
/* * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2024 MonetDB Foundation; * Copyright August 2008 - 2023 MonetDB B.V.; * Copyright 1997 - July 2008 CWI. */ import java.sql.*; import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.sql.Date; import java.text.SimpleDateFormat; import java.util.*; import org.monetdb.jdbc.MonetConnection; import org.monetdb.jdbc.types.INET; import org.monetdb.jdbc.types.URL; /** * class to test JDBC Driver API methods and behavior of MonetDB server. * * It combines 40+ tests which were previous individual test programs * into one large test program, reusing the connection. * This speeds up testing considerably as the overhead of starting a JVM and * loading the java test program class and MonetDB JDBC driver is now reduced * to only one time instead of 40+ times. * Also all output is no longer send to system out/err but collected in a global StringBuilder. * The contents of it is compared with the expected output at the end of each test. * Only when it deviates the output is sent to system err, see compareExpectedOutput(). * * @author Martin van Dinther * @version 0.3 */ public final class JDBC_API_Tester { private StringBuilder sb; // buffer to collect the test output private Connection con; // main connection shared by all tests final private int dbmsMajorVersion; final private int dbmsMinorVersion; final private boolean isPostDec2023; // flag to support version specific output private boolean foundDifferences = false; final private static int sbInitLen = 5468; // max needed size of sb /** * constructor */ JDBC_API_Tester(Connection con_) throws SQLException { this.con = con_; sb = new StringBuilder(sbInitLen); DatabaseMetaData dbmd = con_.getMetaData(); dbmsMajorVersion = dbmd.getDatabaseMajorVersion(); dbmsMinorVersion = dbmd.getDatabaseMinorVersion(); // from version 11.50 on, the MonetDB server returns different metadata for // integer digits (1 less) and for clob and char columns (now return varchar). isPostDec2023 = versionIsAtLeast(11, 50); } /** * main function * @param args args[0] should contain the connectionURL string, args[1] an optional flag: -skipMALoutput * @throws Exception if a connection or database access error occurs */ public static void main(String[] args) throws Exception { if (args.length < 1) { System.err.println("Error: Missing required connection URL as first startup argument!"); System.exit(-1); } final String con_URL = args[0]; final boolean skipMALoutput = (args.length >= 2) ? args[1].equals("-skipMALoutput") : false; // System.err.println("skipMALoutput = " + skipMALoutput); // Test this before trying to connect UrlTester.runAllTests(); final Connection conn = DriverManager.getConnection(con_URL); JDBC_API_Tester jt = new JDBC_API_Tester(conn); // run the tests jt.Test_Cautocommit(con_URL); jt.Test_CisValid(); jt.Test_Clargequery(); jt.Test_Cmanycon(con_URL); jt.Test_Creplysize(); jt.Test_Csavepoints(); jt.Test_Ctransaction(); jt.Test_Dobjects(); jt.Test_DBCmetadata(); jt.Test_EmptySql(); jt.Test_FetchSize(); jt.Test_Int128(); jt.Test_Interval_Types(); jt.Test_PlanExplainTraceDebugCmds(skipMALoutput); jt.Test_PSgeneratedkeys(); jt.Test_PSgetObject(); jt.Test_PSlargebatchval(); jt.Test_PSlargeresponse(con_URL); jt.Test_PSmanycon(con_URL); jt.Test_PSmetadata(); jt.Test_PSsetBytes(); jt.Test_PSsomeamount(); jt.Test_PSsqldata(); jt.Test_PStimedate(); jt.Test_PStimezone(); jt.Test_PStypes(); jt.Test_CallableStmt(); jt.Test_Rbooleans(); jt.Test_Rmetadata(); jt.Test_RfetchManyColumnsInfo(); jt.Test_Rpositioning(); jt.Test_Rsqldata(); jt.Test_Rtimedate(); jt.Test_RSgetMetaData(); jt.Test_Sbatching(); jt.Test_SgeneratedKeys(); jt.Test_Smoreresults(); jt.Test_Wrapper(); if (jt.isPostDec2023) jt.Test_ClientInfo(con_URL); jt.bogus_auto_generated_keys(); jt.BugConcurrent_clients_SF_1504657(con_URL); jt.BugConcurrent_sequences(con_URL); jt.Bug_Connect_as_voc_getMetaData_Failure_Bug_6388(con_URL); jt.BugDatabaseMetaData_Bug_3356(); jt.BugDecimalRound_Bug_3561(); jt.BugExecuteUpdate_Bug_3350(); jt.Bug_IsValid_Timeout_Bug_6782(con_URL); jt.Bug_LargeQueries_6571_6693(con_URL); jt.Bug_PrepStmtManyParams_7337(480); jt.Bug_PrepStmtSetObject_CLOB_6349(); jt.Bug_PrepStmtSetString_6382(); jt.Bug_PrepStmt_With_Errors_Jira292(); jt.BugResultSetMetaData_Bug_6183(); jt.BugSetQueryTimeout_Bug_3357(); jt.SQLcopyinto(con_URL); jt.DecimalPrecisionAndScale(); /* run next long running test (11 minutes) only before a new release */ /* jt.Test_PSlargeamount(); */ jt.closeConx(jt.con); if (jt.foundDifferences) System.exit(-1); ConnectionTests.runTests(con_URL); // invoke running OnClientTester only on Oct2020 (11.39) or older servers if (!jt.versionIsAtLeast(11,40)) { OnClientTester oct = new OnClientTester(con_URL, 0); int failures = oct.runTests(); if (failures > 0) System.exit(-1); } } private boolean versionIsAtLeast(int major, int minor) { return (dbmsMajorVersion > major || (dbmsMajorVersion == major && dbmsMinorVersion >= minor)); } private void Test_Cautocommit(String arg0) { sb.setLength(0); // clear the output log buffer Connection con1 = con; Connection con2 = null; Statement stmt1 = null; Statement stmt2 = null; ResultSet rs = null; try { con2 = DriverManager.getConnection(arg0); // >> true: auto commit should be on by default if (con1.getAutoCommit() != true) sb.append("expecting con1 to have autocommit on/true"); if (con2.getAutoCommit() != true) sb.append("expecting con2 to have autocommit on/true"); // test commit by checking if a change is visible in another connection stmt1 = con1.createStatement(); sb.append("1. create..."); stmt1.executeUpdate("CREATE TABLE table_Test_Cautocommit ( id int )"); sb.append("passed :)\n"); stmt2 = con2.createStatement(); sb.append("2. select..."); rs = stmt2.executeQuery("SELECT * FROM table_Test_Cautocommit"); sb.append("passed :)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); closeStmtResSet(stmt2, rs); closeStmtResSet(stmt1, null); closeConx(con2); sb.append("ABORTING TEST!!!"); return; } try { // turn off auto commit con1.setAutoCommit(false); con2.setAutoCommit(false); // >> false: we just disabled it if (con1.getAutoCommit() != false) sb.append("expecting con1 to have autocommit off/false"); if (con2.getAutoCommit() != false) sb.append("expecting con2 to have autocommit off/false"); // a change would not be visible now sb.append("3. drop..."); stmt2.executeUpdate("DROP TABLE table_Test_Cautocommit"); sb.append("passed :)\n"); sb.append("4. select..."); rs = stmt1.executeQuery("SELECT * FROM table_Test_Cautocommit"); sb.append("passed :)\n"); sb.append("5. commit..."); con2.commit(); sb.append("passed :)\n"); sb.append("6. select..."); rs = stmt1.executeQuery("SELECT * FROM table_Test_Cautocommit"); sb.append("passed :)\n"); sb.append("7. commit..."); con1.commit(); sb.append("passed :)\n"); // restore original auto commit setting con1.setAutoCommit(true); con2.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt1, rs); closeStmtResSet(stmt2, null); closeConx(con2); compareExpectedOutput("Test_Cautocommit", "1. create...passed :)\n" + "2. select...passed :)\n" + "3. drop...passed :)\n" + "4. select...passed :)\n" + "5. commit...passed :)\n" + "6. select...passed :)\n" + "7. commit...passed :)\n"); } private void Test_CisValid() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { stmt = con.createStatement(); con.setAutoCommit(false); // start a transaction stmt.executeQuery("SELECT COUNT(*) FROM doesnotexist;"); // let's trigger an error } catch (SQLException e) { // e.printStackTrace(); sb.append("Expected error: ").append(e).append("\n"); try { // test calling conn.isValid() sb.append("Validating connection: con.isValid? ").append(con.isValid(30)); // Can we rollback on this connection without causing an error? con.rollback(); } catch (SQLException e2) { sb.append("UnExpected error: ").append(e2); } } try { // restore auto commit mode con.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); compareExpectedOutput("Test_CisValid", "Expected error: java.sql.SQLException: SELECT: no such table 'doesnotexist'\n" + "Validating connection: con.isValid? true"); } private void Test_Clargequery() { sb.setLength(0); // clear the output log buffer final String query = "-- When a query larger than the send buffer is being " + "sent, a deadlock situation can occur when the server writes " + "data back, blocking because we as client are sending as well " + "and not reading. Hence, to avoid this deadlock, in JDBC a " + "separate thread is started in the background such that results " + "from the server can be read, while data is still being sent to " + "the server. To test this, we need to trigger the SendThread " + "being started, which we do with a quite large query. We " + "construct it by repeating some stupid query plus a comment " + "a lot of times. And as you're guessing by now, you're reading " + "this stupid comment that we use :)\n" + "select 1;\n"; final int size = 1234; StringBuilder bigq = new StringBuilder(query.length() * size); for (int i = 0; i < size; i++) { bigq.append(query); } Statement stmt = null; try { // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); // sending big script with many simple queries sb.append("1. executing script\n"); stmt.execute(bigq.toString()); int i = 1; // we skip the first "getResultSet()" while (stmt.getMoreResults() != false) { i++; } if (stmt.getUpdateCount() != -1) { sb.append("Error: found an update count for a SELECT query\n"); } if (i != size) { sb.append("Error: expecting ").append(size).append(" tuples, only got ").append(i).append("\n"); } sb.append("2. queries processed\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); compareExpectedOutput("Test_Clargequery", "0. true true\n" + "1. executing script\n" + "2. queries processed\n"); } private void Test_Cmanycon(String arg0) { sb.setLength(0); // clear the output log buffer final int maxCons = 60; // default max_clients is 64, 2 connections are already open from this program List<Connection> cons = new ArrayList<Connection>(maxCons); // Connections go in here try { // spawn a lot of Connections, just for fun... int i = 1; sb.append("Establishing Connection "); for (; i <= maxCons; i++) { sb.append(i); Connection conx = DriverManager.getConnection(arg0); sb.append(","); cons.add(conx); // do something with the connection to test if it works conx.setAutoCommit(false); sb.append(" "); conx.createStatement(); } sb.append("\n"); // now try to nicely close them i = 1; sb.append("Closing Connection "); for (Iterator<Connection> it = cons.iterator(); it.hasNext(); i++) { Connection conx = it.next(); // see if the connection still works sb.append(i); conx.setAutoCommit(true); sb.append(","); conx.close(); // this will also implicitly close the created statement object sb.append(" "); } } catch (SQLException e) { sb.append(" FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_Cmanycon", "Establishing Connection 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, " + "11, 12, 13, 14, 15, 16, 17, 18, 19, 20, " + "21, 22, 23, 24, 25, 26, 27, 28, 29, 30, " + "31, 32, 33, 34, 35, 36, 37, 38, 39, 40, " + "41, 42, 43, 44, 45, 46, 47, 48, 49, 50, " + "51, 52, 53, 54, 55, 56, 57, 58, 59, 60, \n" + "Closing Connection 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, " + "11, 12, 13, 14, 15, 16, 17, 18, 19, 20, " + "21, 22, 23, 24, 25, 26, 27, 28, 29, 30, " + "31, 32, 33, 34, 35, 36, 37, 38, 39, 40, " + "41, 42, 43, 44, 45, 46, 47, 48, 49, 50, " + "51, 52, 53, 54, 55, 56, 57, 58, 59, 60, "); } private void Test_Creplysize() { sb.setLength(0); // clear the output log buffer Statement stmt1 = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> true: auto commit should be off by now sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt1 = con.createStatement(); // test commit by checking if a change is visible in another connection sb.append("1. create... "); stmt1.executeUpdate("CREATE TABLE table_Test_Creplysize ( id int )"); sb.append("passed\n"); sb.append("2. populating with 21 records... "); for (int i = 0; i < 21; i++) stmt1.executeUpdate("INSERT INTO table_Test_Creplysize (id) values (" + (i + 1) + ")"); sb.append("passed\n"); sb.append("3. hinting the driver to use fetchsize 10... "); stmt1.setFetchSize(10); sb.append("passed\n"); sb.append("4. selecting all values... "); rs = stmt1.executeQuery("SELECT * FROM table_Test_Creplysize"); int i = 0; while (rs.next()) i++; rs.close(); if (i == 21) { sb.append("passed"); } else { sb.append("got ").append(i).append(" records!!!"); } sb.append("\n"); sb.append("5. resetting driver fetchsize hint... "); stmt1.setFetchSize(0); sb.append("passed\n"); sb.append("6. instructing the driver to return at max 10 rows... "); stmt1.setMaxRows(10); sb.append("passed\n"); sb.append("7. selecting all values... "); rs = stmt1.executeQuery("SELECT * FROM table_Test_Creplysize"); i = 0; while (rs.next()) i++; rs.close(); if (i == 10) { sb.append("passed"); } else { sb.append("got ").append(i).append(" records!!!"); } sb.append("\n"); sb.append("8. hinting the driver to use fetchsize 5... "); stmt1.setFetchSize(5); sb.append("passed\n"); sb.append("9. selecting all values... "); rs = stmt1.executeQuery("SELECT * FROM table_Test_Creplysize"); i = 0; while (rs.next()) i++; rs.close(); if (i == 10) { sb.append("passed"); } else { sb.append("got ").append(i).append(" records!!!"); } sb.append("\n"); sb.append("10. drop... "); stmt1.executeUpdate("DROP TABLE table_Test_Creplysize"); sb.append("passed\n"); con.rollback(); // restore auto commit mode con.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt1, rs); compareExpectedOutput("Test_Creplysize", "0. true false\n" + "1. create... passed\n" + "2. populating with 21 records... passed\n" + "3. hinting the driver to use fetchsize 10... passed\n" + "4. selecting all values... passed\n" + "5. resetting driver fetchsize hint... passed\n" + "6. instructing the driver to return at max 10 rows... passed\n" + "7. selecting all values... passed\n" + "8. hinting the driver to use fetchsize 5... passed\n" + "9. selecting all values... passed\n" + "10. drop... passed\n"); } private void Test_Csavepoints() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); // savepoints require a non-autocommit connection try { sb.append("1. savepoint..."); con.setSavepoint(); sb.append("passed !!"); } catch (SQLException e) { sb.append("expected msg: ").append(e.getMessage()); } sb.append("\n"); con.setAutoCommit(false); // >> true: auto commit should be on by default sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); sb.append("2. savepoint..."); /* make a savepoint, and discard it */ con.setSavepoint(); sb.append("passed\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE table_Test_Csavepoints ( id int, PRIMARY KEY (id) )"); sb.append("3. savepoint..."); Savepoint sp2 = con.setSavepoint("empty table"); sb.append("passed\n"); rs = stmt.executeQuery("SELECT id FROM table_Test_Csavepoints"); int i = 0; int items = 0; sb.append("4. table ").append(items).append(" items"); while (rs.next()) { sb.append(", ").append(rs.getString("id")); i++; } if (i != items) { sb.append(" FAILED (").append(i).append(")"); } sb.append(" passed\n"); stmt.executeUpdate("INSERT INTO table_Test_Csavepoints VALUES (1)"); stmt.executeUpdate("INSERT INTO table_Test_Csavepoints VALUES (2)"); stmt.executeUpdate("INSERT INTO table_Test_Csavepoints VALUES (3)"); sb.append("5. savepoint..."); Savepoint sp3 = con.setSavepoint("three values"); sb.append("passed\n"); rs = stmt.executeQuery("SELECT id FROM table_Test_Csavepoints"); i = 0; items = 3; sb.append("6. table ").append(items).append(" items"); while (rs.next()) { sb.append(", ").append(rs.getString("id")); i++; } if (i != items) { sb.append(" FAILED (").append(i).append(")"); } sb.append(" passed\n"); sb.append("7. release..."); con.releaseSavepoint(sp3); sb.append("passed\n"); rs = stmt.executeQuery("SELECT id FROM table_Test_Csavepoints"); i = 0; items = 3; sb.append("8. table ").append(items).append(" items"); while (rs.next()) { sb.append(", ").append(rs.getString("id")); i++; } if (i != items) { sb.append(" FAILED (").append(i).append(") :("); } sb.append(" passed\n"); sb.append("9. rollback..."); con.rollback(sp2); sb.append("passed\n"); rs = stmt.executeQuery("SELECT id FROM table_Test_Csavepoints"); i = 0; items = 0; sb.append("10. table ").append(items).append(" items"); while (rs.next()) { sb.append(", ").append(rs.getString("id")); i++; } if (i != items) { sb.append(" FAILED (").append(i).append(") :("); } sb.append(" passed"); con.rollback(); // restore auto commit mode con.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Csavepoints", "0. true true\n" + "1. savepoint...expected msg: SAVEPOINT: not allowed in auto commit mode\n" + "0. false false\n" + "2. savepoint...passed\n" + "3. savepoint...passed\n" + "4. table 0 items passed\n" + "5. savepoint...passed\n" + "6. table 3 items, 1, 2, 3 passed\n" + "7. release...passed\n" + "8. table 3 items, 1, 2, 3 passed\n" + "9. rollback...passed\n" + "10. table 0 items passed"); } private void Test_Ctransaction() { sb.setLength(0); // clear the output log buffer try { // test commit by checking if a change is visible in another connection sb.append("1. commit..."); con.commit(); sb.append("passed"); } catch (SQLException e) { // this means we get what we expect sb.append("failed as expected: ").append(e.getMessage()); } sb.append("\n"); try { // turn off auto commit con.setAutoCommit(false); // >> false: we just disabled it sb.append("2. false\t").append(con.getAutoCommit()).append("\n"); // a change would not be visible now sb.append("3. commit..."); con.commit(); sb.append("passed\n"); sb.append("4. commit..."); con.commit(); sb.append("passed\n"); sb.append("5. rollback..."); con.rollback(); sb.append("passed"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()); } sb.append("\n"); Statement stmt = null; try { // turn on auto commit con.setAutoCommit(true); // >> false: we just disabled it sb.append("6. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("7. start transaction..."); stmt.executeUpdate("START TRANSACTION"); sb.append("passed\n"); sb.append("8. commit..."); con.commit(); sb.append("passed\n"); sb.append("9. true\t").append(con.getAutoCommit()); sb.append("\n"); sb.append("10. start transaction..."); stmt.executeUpdate("START TRANSACTION"); sb.append("passed\n"); sb.append("11. rollback..."); con.rollback(); sb.append("passed\n"); sb.append("12. true\t").append(con.getAutoCommit()); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()); } sb.append("\n"); try { // a commit now should fail sb.append("13. commit..."); con.commit(); sb.append("passed"); } catch (SQLException e) { // this means we get what we expect sb.append("failed as expected: ").append(e.getMessage()); } sb.append("\n"); closeStmtResSet(stmt, null); compareExpectedOutput("Test_Ctransaction", "1. commit...failed as expected: COMMIT: not allowed in auto commit mode\n" + "2. false false\n" + "3. commit...passed\n" + "4. commit...passed\n" + "5. rollback...passed\n" + "6. true true\n" + "7. start transaction...passed\n" + "8. commit...passed\n" + "9. true true\n" + "10. start transaction...passed\n" + "11. rollback...passed\n" + "12. true true\n" + "13. commit...failed as expected: COMMIT: not allowed in auto commit mode\n"); } private void handleExecuteDDL(Statement stmt, String action, String objtype, String objname, String sql) { try { int response = stmt.executeUpdate(sql); if (response != Statement.SUCCESS_NO_INFO) sb.append(action).append(" ").append(objtype).append(" ").append(objname).append(" failed to return -2!! It returned: ").append(response).append("\n"); } catch (SQLException e) { sb.append("Failed to ").append(action).append(" ").append(objtype).append(" ").append(objname).append(": ").append(e.getMessage()).append("\n"); } } private void Test_Dobjects() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { stmt = con.createStatement(); } catch (SQLException e) { sb.append("failed to createStatement: ").append(e.getMessage()).append("\n"); } String action = "Create"; final String objtype = "table"; handleExecuteDDL(stmt, action, objtype, "nopk_twoucs", "CREATE TABLE nopk_twoucs (id INT NOT NULL UNIQUE, name VARCHAR(99) UNIQUE)"); handleExecuteDDL(stmt, action, objtype, "tmp_nopk_twoucs", "CREATE LOCAL TEMP TABLE tmp_nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE)"); handleExecuteDDL(stmt, action, objtype, "tmp_pk_uc", "CREATE LOCAL TEMP TABLE tmp_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE)"); handleExecuteDDL(stmt, action, objtype, "glbl_nopk_twoucs", "CREATE GLOBAL TEMP TABLE glbl_nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE)"); handleExecuteDDL(stmt, action, objtype, "glbl_pk_uc", "CREATE GLOBAL TEMP TABLE glbl_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE)"); handleExecuteDDL(stmt, action, "type", "xml", "CREATE TYPE xml EXTERNAL NAME xml"); try { DatabaseMetaData dbmd = con.getMetaData(); // inspect the catalog by use of dbmd functions compareResultSet(dbmd.getCatalogs(), "getCatalogs()", "Resultset with 1 columns\n" + "TABLE_CAT\n" + "char(1)\n"); compareResultSet(dbmd.getSchemas(null, "sys"), "getSchemas(null, sys)", "Resultset with 2 columns\n" + "TABLE_SCHEM TABLE_CATALOG\n" + "varchar(1024) char(1)\n" + "sys null\n"); compareResultSet(dbmd.getTables(null, "tmp", null, null), "getTables(null, tmp, null, null)", // schema tmp has 6 system tables and 4 temporary test tables "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n" + "null tmp glbl_nopk_twoucs GLOBAL TEMPORARY TABLE null null null null null null\n" + "null tmp glbl_pk_uc GLOBAL TEMPORARY TABLE null null null null null null\n" + "null tmp tmp_nopk_twoucs LOCAL TEMPORARY TABLE null null null null null null\n" + "null tmp tmp_pk_uc LOCAL TEMPORARY TABLE null null null null null null\n" + "null tmp _columns SYSTEM TABLE null null null null null null\n" + "null tmp _tables SYSTEM TABLE null null null null null null\n" + "null tmp idxs SYSTEM TABLE null null null null null null\n" + "null tmp keys SYSTEM TABLE null null null null null null\n" + "null tmp objects SYSTEM TABLE null null null null null null\n" + "null tmp triggers SYSTEM TABLE null null null null null null\n"); compareResultSet(dbmd.getTables(null, "sys", "schemas", null), "getTables(null, sys, schemas, null)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n" + "null sys schemas SYSTEM TABLE null null null null null null\n"); compareResultSet(dbmd.getColumns(null, "sys", "table\\_types", null), "getColumns(null, sys, table\\_types, null)", "Resultset with 24 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS NUM_PREC_RADIX NULLABLE REMARKS COLUMN_DEF SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SCOPE_CATALOG SCOPE_SCHEMA SCOPE_TABLE SOURCE_DATA_TYPE IS_AUTOINCREMENT IS_GENERATEDCOLUMN\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) int varchar(1024) int int int int int varchar(65000) varchar(2048) int int bigint int varchar(3) char(1) char(1) char(1) smallint " + (isPostDec2023 ? "var" : "") + "char(3) varchar(3)\n" + "null sys table_types table_type_id 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 2 0 null null 0 0 null 1 NO null null null null NO NO\n" + "null sys table_types table_type_name 12 varchar 25 0 0 0 0 null null 0 0 100 2 NO null null null null NO NO\n"); compareResultSet(dbmd.getPrimaryKeys(null, "sys", "table\\_types"), "getPrimaryKeys(null, sys, table\\_types)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null sys table_types table_type_id 1 table_types_table_type_id_pkey\n"); compareResultSet(dbmd.getPrimaryKeys(null, "tmp", "tmp_pk_uc"), "getPrimaryKeys(null, tmp, tmp_pk_uc)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null tmp tmp_pk_uc id1 1 tmp_pk_uc_id1_pkey\n"); compareResultSet(dbmd.getPrimaryKeys(null, "tmp", "glbl_pk_uc"), "getPrimaryKeys(null, tmp, glbl_pk_uc)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null tmp glbl_pk_uc id1 1 glbl_pk_uc_id1_pkey\n"); compareResultSet(dbmd.getExportedKeys(null, "sys", "table\\_types"), "getExportedKeys(null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getCrossReference(null, "sys", "tables", null, "sys", "table\\_types"), "getCrossReference(null, sys, tables, null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getImportedKeys(null, "sys", "table\\_types"), "getImportedKeys(null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getIndexInfo(null, "sys", "key_types", false, false), "getIndexInfo(null, sys, key_types, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null sys key_types false null key_types_key_type_id_pkey 2 1 key_type_id null " + (isPostDec2023 ? "5" : "3") + " 0 null\n" + "null sys key_types false null key_types_key_type_name_unique 2 1 key_type_name null " + (isPostDec2023 ? "5" : "3") + " 0 null\n"); compareResultSet(dbmd.getIndexInfo(null, "tmp", "tmp_pk_uc", false, false), "getIndexInfo(null, tmp, tmp_pk_uc, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null tmp tmp_pk_uc false null tmp_pk_uc_id1_pkey 2 1 id1 null 0 0 null\n" + "null tmp tmp_pk_uc false null tmp_pk_uc_name1_unique 2 1 name1 null 0 0 null\n"); compareResultSet(dbmd.getIndexInfo(null, "tmp", "glbl_pk_uc", false, false), "getIndexInfo(null, tmp, glbl_pk_uc, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null tmp glbl_pk_uc false null glbl_pk_uc_id1_pkey 2 1 id1 null 0 0 null\n" + "null tmp glbl_pk_uc false null glbl_pk_uc_name1_unique 2 1 name1 null 0 0 null\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "function_languages", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, function_languages, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 language_id 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, sys, nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_pk_uc", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, tmp_pk_uc, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id1 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, tmp_nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, tmp, tmp_nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_pk_uc", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, glbl_pk_uc, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id1 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, glbl_nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, tmp, glbl_nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getTablePrivileges(null, "sys", "table\\_types"), "getTablePrivileges(null, sys, table\\_types)", "Resultset with 7 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME GRANTOR GRANTEE PRIVILEGE IS_GRANTABLE\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(40) varchar(3)\n" + "null sys table_types monetdb public SELECT NO\n"); compareResultSet(dbmd.getColumnPrivileges(null, "sys", "table\\_types", null), "getColumnPrivileges(null, sys, table\\_types, null)", "Resultset with 8 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME GRANTOR GRANTEE PRIVILEGE IS_GRANTABLE\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(40) varchar(3)\n"); compareResultSet(dbmd.getUDTs(null, "sys", null, null), "getUDTs(null, sys, null, null)", "Resultset with 7 columns\n" + "TYPE_CAT TYPE_SCHEM TYPE_NAME CLASS_NAME DATA_TYPE REMARKS BASE_TYPE\n" + "char(1) varchar(1024) varchar(1024) " + (isPostDec2023 ? "var" : "") + "char(16) int varchar(256) smallint\n" + "null sys xml java.lang.String 2000 xml null\n"); int[] UDTtypes = { Types.STRUCT, Types.DISTINCT }; compareResultSet(dbmd.getUDTs(null, "sys", null, UDTtypes), "getUDTs(null, sys, null, UDTtypes", "Resultset with 7 columns\n" + "TYPE_CAT TYPE_SCHEM TYPE_NAME CLASS_NAME DATA_TYPE REMARKS BASE_TYPE\n" + "char(1) varchar(1024) varchar(1024) " + (isPostDec2023 ? "var" : "") + "char(16) int varchar(256) smallint\n"); sb.setLength(0); // clear the output log buffer } catch (SQLException e) { sb.setLength(0); // clear the output log buffer sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup created db objects action = "Drop"; handleExecuteDDL(stmt, action, objtype, "nopk_twoucs", "DROP TABLE nopk_twoucs"); handleExecuteDDL(stmt, action, objtype, "tmp_nopk_twoucs", "DROP TABLE tmp_nopk_twoucs"); handleExecuteDDL(stmt, action, objtype, "tmp_pk_uc", "DROP TABLE tmp_pk_uc"); handleExecuteDDL(stmt, action, objtype, "glbl_nopk_twoucs", "DROP TABLE glbl_nopk_twoucs"); handleExecuteDDL(stmt, action, objtype, "glbl_pk_uc", "DROP TABLE glbl_pk_uc"); handleExecuteDDL(stmt, action, "type", "xml", "DROP TYPE xml"); closeStmtResSet(stmt, null); compareExpectedOutput("Test_Dobjects", ""); } private void compareResultSet(ResultSet rs, String methodnm, String expected) throws SQLException { sb.setLength(0); // clear the output log buffer ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); int suppressSPECIFIC_NAME = 0; sb.append("Resultset with ").append(columnCount).append(" columns\n"); // print result column header names for (int col = 1; col <= columnCount; col++) { if (col > 1) sb.append("\t"); sb.append(rsmd.getColumnName(col)); if ("SPECIFIC_NAME".equals(rsmd.getColumnName(col))) suppressSPECIFIC_NAME = col; // contains internal id values which change } sb.append("\n"); // print result column data type info for (int col = 1; col <= columnCount; col++) { if (col > 1) sb.append("\t"); sb.append(rsmd.getColumnTypeName(col)); switch (rsmd.getColumnType(col)) { case Types.CHAR: case Types.VARCHAR: case Types.CLOB: case Types.BLOB: case Types.DECIMAL: case Types.NUMERIC: { int prec = rsmd.getPrecision(col); if (prec != 0) { sb.append('(').append(prec); int scale = rsmd.getScale(col); if (scale != 0) { sb.append(',').append(scale); } sb.append(')'); } } } } sb.append("\n"); // print result rows data while (rs.next()) { for (int col = 1; col <= columnCount; col++) { if (col > 1) sb.append("\t"); if (col == suppressSPECIFIC_NAME) sb.append("suppressed"); else sb.append(rs.getString(col)); } sb.append("\n"); } rs.close(); compareExpectedOutput(methodnm, expected); } // same tests as done in clients/odbc/tests/ODBCmetadata.c private void Test_DBCmetadata() { sb.setLength(0); // clear the output log buffer Statement stmt = null; DatabaseMetaData dbmd = null; try { stmt = con.createStatement(); dbmd = con.getMetaData(); } catch (SQLException e) { sb.append("Failed to createStatement: ").append(e.getMessage()).append("\n"); } try { // test dbmd methods which sent a query to the server and return a String. String s = dbmd.getSQLKeywords(); if (s == null || s.length() < 10) sb.append("getSQLKeywords()").append(" failed to return a large enough list\n"); s = dbmd.getNumericFunctions(); if (s == null || s.length() < 10) sb.append("getNumericFunctions()").append(" failed to return a large enough list\n"); s = dbmd.getStringFunctions(); if (s == null || s.length() < 10) sb.append("getStringFunctions()").append(" failed to return a large enough list\n"); s = dbmd.getSystemFunctions(); if (s == null || s.length() < 10) sb.append("getSystemFunctions()").append(" failed to return a large enough list\n"); s = dbmd.getTimeDateFunctions(); if (s == null || s.length() < 10) sb.append("getTimeDateFunctions()").append(" failed to return a large enough list\n"); s = dbmd.getUserName(); if (s == null || s.length() < 1) sb.append("getUserName() failed to return a valid name\n"); s = dbmd.getDatabaseProductVersion(); if (s == null || s.length() < 1) sb.append("getDatabaseProductVersion() failed to return a valid version\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_DBCmetadata() testing dbmd.get...Functions() as String", ""); sb.setLength(0); // clear the output log buffer /* servers before Jan2022 (11.45) have a problem (server crash and db corruption) with indexes created on temp tables, so conditionally execute those commands and tests */ final boolean testCreateDropIndexOnTmpTables = !(dbmsMajorVersion == 11 && dbmsMinorVersion <= 43); String action = "Create"; handleExecuteDDL(stmt, action, "schema", "jdbctst", "CREATE SCHEMA jdbctst; SET SCHEMA jdbctst;"); String objtype = "table"; // create tables to populate data dictionary. Used for testing getTables(), // getColumns(), getBestRowIdentifier(), getPrimaryKeys(), // getExportedKeys(), getImportedKeys(), getCrossReference() handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc", "CREATE TABLE jdbctst.pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc", "CREATE LOCAL TEMP TABLE tmp.tmp_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_pk_uc", "CREATE GLOBAL TEMP TABLE tmp.glbl_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs", "CREATE TABLE jdbctst.nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_nopk_twoucs", "CREATE LOCAL TEMP TABLE tmp.tmp_nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_nopk_twoucs", "CREATE GLOBAL TEMP TABLE tmp.glbl_nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE);"); handleExecuteDDL(stmt, action, objtype, "tmp.tlargechar", "CREATE TEMP TABLE tlargechar (c1 varchar(2147483647), c2 char(2147483646), c3 clob(2147483645), c4 json(2147483644), c5 url(2147483643)) ON COMMIT PRESERVE ROWS;"); /* next 3 tables copied from example in https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function?view=sql-server-ver15 */ handleExecuteDDL(stmt, action, objtype, "\"CUSTOMERS\"", "CREATE TABLE \"CUSTOMERS\" (\"CUSTID\" INT PRIMARY KEY, \"NAME\" VARCHAR(60) NOT NULL, \"ADDRESS\" VARCHAR(90), \"PHONE\" VARCHAR(20));"); handleExecuteDDL(stmt, action, objtype, "\"ORDERS\"", "CREATE TABLE \"ORDERS\" (\"ORDERID\" INT PRIMARY KEY, \"CUSTID\" INT NOT NULL REFERENCES \"CUSTOMERS\" (\"CUSTID\")" + ", \"OPENDATE\" DATE NOT NULL, \"SALESPERSON\" VARCHAR(60), \"STATUS\" VARCHAR(10) NOT NULL);"); handleExecuteDDL(stmt, action, objtype, "\"LINES\"", "CREATE TABLE \"LINES\" (\"ORDERID\" INT NOT NULL REFERENCES \"ORDERS\" (\"ORDERID\"), \"LINES\" INT" + ", PRIMARY KEY (\"ORDERID\", \"LINES\"), \"PARTID\" INT NOT NULL, \"QUANTITY\" DECIMAL(9,3) NOT NULL);"); /* also test situation where one table has multiple fks to the same multi column pk */ handleExecuteDDL(stmt, action, objtype, "jdbctst.pk2c", "CREATE TABLE jdbctst.pk2c (pkc1 INT, pkc2 VARCHAR(99), name1 VARCHAR(99) UNIQUE, PRIMARY KEY (pkc2, pkc1));"); handleExecuteDDL(stmt, action, objtype, "jdbctst.fk2c", "CREATE TABLE jdbctst.fk2c (fkc1 INT NOT NULL PRIMARY KEY, fkc2 VARCHAR(99), fkc3 INT" + ", FOREIGN KEY (fkc2, fkc1) REFERENCES jdbctst.pk2c (pkc2, pkc1) ON UPDATE CASCADE ON DELETE RESTRICT" + ", FOREIGN KEY (fkc2, fkc3) REFERENCES jdbctst.pk2c (pkc2, pkc1) ON UPDATE SET NULL ON DELETE NO ACTION);"); // create indexes to populate catalog. Used for testing getIndexInfo() objtype = "index"; handleExecuteDDL(stmt, action, objtype, "pk_uc_i", "CREATE INDEX pk_uc_i ON jdbctst.pk_uc (id1, name1);"); handleExecuteDDL(stmt, action, objtype, "nopk_twoucs_i", "CREATE INDEX nopk_twoucs_i ON jdbctst.nopk_twoucs (id2, name2);"); if (testCreateDropIndexOnTmpTables) { handleExecuteDDL(stmt, action, objtype, "tmp_pk_uc_i", "CREATE INDEX tmp_pk_uc_i ON tmp.tmp_pk_uc (id1, name1);"); handleExecuteDDL(stmt, action, objtype, "glbl_pk_uc_i", "CREATE INDEX glbl_pk_uc_i ON tmp.glbl_pk_uc (id1, name1);"); handleExecuteDDL(stmt, action, objtype, "tmp_nopk_twoucs_i", "CREATE INDEX tmp_nopk_twoucs_i ON tmp.tmp_nopk_twoucs (id2, name2);"); handleExecuteDDL(stmt, action, objtype, "glbl_nopk_twoucs_i", "CREATE INDEX glbl_nopk_twoucs_i ON tmp.glbl_nopk_twoucs (id2, name2);"); } // grant privileges to populate catalog. Used for testing getTablePrivileges() and getColumnPrivileges() action = "grant"; objtype = "table"; handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc", "GRANT SELECT ON TABLE jdbctst.pk_uc TO PUBLIC;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc", "GRANT INSERT, UPDATE, DELETE ON TABLE jdbctst.pk_uc TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs", "GRANT SELECT (id2, name2), UPDATE (name2) ON TABLE jdbctst.nopk_twoucs TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc", "GRANT INSERT, DELETE ON TABLE tmp.tmp_pk_uc TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc", "GRANT SELECT (id1, name1), UPDATE (name1) ON TABLE tmp.tmp_pk_uc TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc ", "GRANT INSERT, DELETE ON TABLE tmp.tmp_pk_uc TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_pk_uc", "GRANT SELECT (id1, name1), UPDATE (name1) ON TABLE tmp.glbl_pk_uc TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_nopk_twoucs", "GRANT INSERT, DELETE ON TABLE tmp.tmp_nopk_twoucs TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_nopk_twoucs", "GRANT SELECT (id2, name2), UPDATE (name2) ON TABLE tmp.tmp_nopk_twoucs TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_nopk_twoucs", "GRANT DELETE, INSERT ON TABLE tmp.glbl_nopk_twoucs TO monetdb;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_nopk_twoucs", "GRANT SELECT (id2, name2), UPDATE (name2) ON TABLE tmp.glbl_nopk_twoucs TO monetdb;"); // create user procedure to test getProcedures() and getProcedureColumns() action = "create"; objtype = "procedure"; handleExecuteDDL(stmt, action, objtype, "jdbctst.proc", "CREATE PROCEDURE jdbctst.proc(id bigint, name varchar(999), dt date) BEGIN SET SCHEMA jdbctst; /* ... */ END;"); // create user function to test getFunctions() and getFunctionColumns() action = "create"; objtype = "function"; handleExecuteDDL(stmt, action, objtype, "jdbctst.func", "CREATE FUNCTION jdbctst.func(id bigint, name varchar(999), dt date) RETURNS TABLE (val varchar(1022), dt date, id int) BEGIN return SELECT id||' '||name ||' '|| dt, dt, id WHERE id > 0; END;"); // set COMMENT ON schema, tables, columns, indexes, procedures and functions to fetch (and test) data in the REMARKS result column action = "comment on"; handleExecuteDDL(stmt, action, "schema", "jdbctst", "COMMENT ON SCHEMA jdbctst IS 'jdbctst schema comment';"); objtype = "table"; handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc", "COMMENT ON TABLE jdbctst.pk_uc IS 'jdbctst.pk_uc table comment';"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs", "COMMENT ON TABLE jdbctst.nopk_twoucs IS 'jdbctst.nopk_twoucs table comment';"); objtype = "column"; handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs.id2", "COMMENT ON COLUMN jdbctst.nopk_twoucs.id2 IS 'jdbctst.nopk_twoucs.id2 column comment';"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs.name2", "COMMENT ON COLUMN jdbctst.nopk_twoucs.name2 IS 'jdbctst.nopk_twoucs.name2 column comment';"); objtype = "index"; handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc_i", "COMMENT ON INDEX jdbctst.pk_uc_i IS 'jdbctst.pk_uc_i index comment';"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs_i", "COMMENT ON INDEX jdbctst.nopk_twoucs_i IS 'jdbctst.nopk_twoucs_i index comment';"); objtype = "procedure"; handleExecuteDDL(stmt, action, objtype, "sys.analyze()", "COMMENT ON PROCEDURE sys.analyze() IS 'sys.analyze() procedure comment';"); handleExecuteDDL(stmt, action, objtype, "jdbctst.proc", "COMMENT ON PROCEDURE jdbctst.proc IS 'jdbctst.proc procedure comment';"); objtype = "function"; handleExecuteDDL(stmt, action, objtype, "sys.sin(double)", "COMMENT ON FUNCTION sys.sin(double) IS 'sys.sin(double) function comment';"); handleExecuteDDL(stmt, action, objtype, "sys.env()", "COMMENT ON FUNCTION sys.env() IS 'sys.env() function comment';"); handleExecuteDDL(stmt, action, objtype, "sys.statistics()", "COMMENT ON FUNCTION sys.statistics() IS 'sys.statistics() function comment';"); handleExecuteDDL(stmt, action, objtype, "jdbctst.func", "COMMENT ON FUNCTION jdbctst.func IS 'jdbctst.func function comment';"); try { // query the catalog by calling DatabaseMetaData methods compareResultSet(dbmd.getCatalogs(), "getCatalogs()", "Resultset with 1 columns\n" + "TABLE_CAT\n" + "char(1)\n"); compareResultSet(dbmd.getSchemas(null, "jdbctst"), "getSchemas(null, jdbctst)", "Resultset with 2 columns\n" + "TABLE_SCHEM TABLE_CATALOG\n" + "varchar(1024) char(1)\n" + "jdbctst null\n"); compareResultSet(dbmd.getSchemas(null, ""), "getSchemas(null, emptystring)", "Resultset with 2 columns\n" + "TABLE_SCHEM TABLE_CATALOG\n" + "varchar(1024) char(1)\n"); compareResultSet(dbmd.getSchemas("%", "%%"), "getSchemas(%, %%)", "Resultset with 2 columns\n" + "TABLE_SCHEM TABLE_CATALOG\n" + "varchar(1024) char(1)\n"); compareResultSet(dbmd.getTables(null, "jdbctst", null, null), "getTables(null, jdbctst, null, null)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n" + "null jdbctst CUSTOMERS TABLE null null null null null null\n" + "null jdbctst LINES TABLE null null null null null null\n" + "null jdbctst ORDERS TABLE null null null null null null\n" + "null jdbctst fk2c TABLE null null null null null null\n" + "null jdbctst nopk_twoucs TABLE jdbctst.nopk_twoucs table comment null null null null null\n" + "null jdbctst pk2c TABLE null null null null null null\n" + "null jdbctst pk_uc TABLE jdbctst.pk_uc table comment null null null null null\n"); String tabletypes[] = {"BASE TABLE", "LOCAL TEMPORARY", "GLOBAL TEMPORARY"}; compareResultSet(dbmd.getTables(null, "tmp", "tlargechar", tabletypes), "getTables(null, tmp, tlargechar, tabletypes)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n" + "null tmp tlargechar LOCAL TEMPORARY TABLE null null null null null null\n"); String badtabletypes[] = {null, "", null, ""}; compareResultSet(dbmd.getTables(null, "tmp", "tlargechar", badtabletypes), "getTables(null, tmp, tlargechar, badtabletypes)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n"); compareResultSet(dbmd.getTables(null, "jdbctst", "schemas", null), "getTables(null, jdbctst, schemas, null)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n"); compareResultSet(dbmd.getColumns(null, "jdbctst", "pk\\_uc", null), "getColumns(null, jdbctst, pk\\_uc, null)", "Resultset with 24 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS NUM_PREC_RADIX NULLABLE REMARKS COLUMN_DEF SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SCOPE_CATALOG SCOPE_SCHEMA SCOPE_TABLE SOURCE_DATA_TYPE IS_AUTOINCREMENT IS_GENERATEDCOLUMN\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) int varchar(1024) int int int int int varchar(65000) varchar(2048) int int bigint int varchar(3) char(1) char(1) char(1) smallint " + (isPostDec2023 ? "var" : "") + "char(3) varchar(3)\n" + "null jdbctst pk_uc id1 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 2 0 null null 0 0 null 1 NO null null null null NO NO\n" + "null jdbctst pk_uc name1 12 varchar 99 0 0 0 1 null null 0 0 396 2 YES null null null null NO NO\n"); compareResultSet(dbmd.getColumns(null, "tmp", "tlargechar", null), "getColumns(null, tmp, tlargechar, null)", "Resultset with 24 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS NUM_PREC_RADIX NULLABLE REMARKS COLUMN_DEF SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SCOPE_CATALOG SCOPE_SCHEMA SCOPE_TABLE SOURCE_DATA_TYPE IS_AUTOINCREMENT IS_GENERATEDCOLUMN\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) int varchar(1024) int int int int int varchar(65000) varchar(2048) int int bigint int varchar(3) char(1) char(1) char(1) smallint " + (isPostDec2023 ? "var" : "") + "char(3) varchar(3)\n" + "null tmp tlargechar c1 12 varchar 2147483647 0 0 0 1 null null 0 0 8589934588 1 YES null null null null NO NO\n" + "null tmp tlargechar c2 1 char 2147483646 0 0 0 1 null null 0 0 8589934584 2 YES null null null null NO NO\n" + "null tmp tlargechar c3 " + (isPostDec2023 ? "12 varchar" : "2005 clob") + " 2147483645 0 0 0 1 null null 0 0 8589934580 3 YES null null null null NO NO\n" + "null tmp tlargechar c4 12 json 2147483644 0 0 0 1 null null 0 0 8589934576 4 YES null null null null NO NO\n" + "null tmp tlargechar c5 12 url 2147483643 0 0 0 1 null null 0 0 8589934572 5 YES null null null null NO NO\n"); compareResultSet(dbmd.getPrimaryKeys(null, "jdbctst", "pk\\_uc"), "getPrimaryKeys(null, jdbctst, pk\\_uc)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null jdbctst pk_uc id1 1 pk_uc_id1_pkey\n"); /* MvD: hier verder */ compareResultSet(dbmd.getPrimaryKeys(null, "tmp", "tmp_pk_uc"), "getPrimaryKeys(null, tmp, tmp_pk_uc)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null tmp tmp_pk_uc id1 1 tmp_pk_uc_id1_pkey\n"); compareResultSet(dbmd.getPrimaryKeys(null, "tmp", "glbl_pk_uc"), "getPrimaryKeys(null, tmp, glbl_pk_uc)", "Resultset with 6 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) smallint varchar(1024)\n" + "null tmp glbl_pk_uc id1 1 glbl_pk_uc_id1_pkey\n"); compareResultSet(dbmd.getExportedKeys(null, "sys", "table\\_types"), "getExportedKeys(null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getCrossReference(null, "sys", "tables", null, "sys", "table\\_types"), "getCrossReference(null, sys, tables, null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getImportedKeys(null, "sys", "table\\_types"), "getImportedKeys(null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) char(1) varchar(1024) varchar(1024) varchar(1024) smallint smallint smallint varchar(1024) varchar(1024) smallint\n"); compareResultSet(dbmd.getIndexInfo(null, "sys", "key_types", false, false), "getIndexInfo(null, sys, key_types, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null sys key_types false null key_types_key_type_id_pkey 2 1 key_type_id null " + (isPostDec2023 ? "5" : "3") + " 0 null\n" + "null sys key_types false null key_types_key_type_name_unique 2 1 key_type_name null " + (isPostDec2023 ? "5" : "3") + " 0 null\n"); if (testCreateDropIndexOnTmpTables) { compareResultSet(dbmd.getIndexInfo(null, "tmp", "tmp_pk_uc", false, false), "getIndexInfo(null, tmp, tmp_pk_uc, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null tmp tmp_pk_uc false null tmp_pk_uc_id1_pkey 2 1 id1 null 0 0 null\n" + "null tmp tmp_pk_uc false null tmp_pk_uc_name1_unique 2 1 name1 null 0 0 null\n" + "null tmp tmp_pk_uc true null tmp_pk_uc_i 2 1 id1 null 0 0 null\n" + "null tmp tmp_pk_uc true null tmp_pk_uc_i 2 2 name1 null 0 0 null\n"); } if (testCreateDropIndexOnTmpTables) { compareResultSet(dbmd.getIndexInfo(null, "tmp", "glbl_pk_uc", false, false), "getIndexInfo(null, tmp, glbl_pk_uc, false, false)", "Resultset with 13 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME NON_UNIQUE INDEX_QUALIFIER INDEX_NAME TYPE ORDINAL_POSITION COLUMN_NAME ASC_OR_DESC CARDINALITY PAGES FILTER_CONDITION\n" + "char(1) varchar(1024) varchar(1024) boolean char(1) varchar(1024) tinyint smallint varchar(1024) char(1) int int char(1)\n" + "null tmp glbl_pk_uc false null glbl_pk_uc_id1_pkey 2 1 id1 null 0 0 null\n" + "null tmp glbl_pk_uc false null glbl_pk_uc_name1_unique 2 1 name1 null 0 0 null\n" + "null tmp glbl_pk_uc true null glbl_pk_uc_i 2 1 id1 null 0 0 null\n" + "null tmp glbl_pk_uc true null glbl_pk_uc_i 2 2 name1 null 0 0 null\n"); } compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "function_languages", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, function_languages, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 language_id 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "jdbctst", "nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, jdbctst, nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "jdbctst", "nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, jdbctst, nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_pk_uc", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, tmp_pk_uc, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id1 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, tmp_nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "tmp_nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, tmp, tmp_nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_pk_uc", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, glbl_pk_uc, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id1 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_nopk_twoucs", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, tmp, glbl_nopk_twoucs, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "tmp", "glbl_nopk_twoucs", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, tmp, glbl_nopk_twoucs, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id2 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n"); // also test a table without pk or uc, it should return a row for each column compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "schemas", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, schemas, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n" + "2 name 12 varchar 1024 0 0 1\n" + "2 authorization 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n" + "2 owner 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n" + "2 system 16 boolean 1 0 0 1\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "_tables", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, _tables, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 id 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n" + "2 name 12 varchar 1024 0 0 1\n" + "2 schema_id 4 int " + (isPostDec2023 ? "31" : "32") + " 0 0 1\n" + "2 query 12 varchar 1048576 0 0 1\n" + "2 type 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n" + "2 system 16 boolean 1 0 0 1\n" + "2 commit_action 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n" + "2 access 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n"); // also test a view (without pk or uc of course), it should return no rows compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "tables", DatabaseMetaData.bestRowTransaction, true), "getBestRowIdentifier(null, sys, tables, DatabaseMetaData.bestRowTransaction, true)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n"); compareResultSet(dbmd.getBestRowIdentifier(null, "sys", "table\\_types", DatabaseMetaData.bestRowTransaction, false), "getBestRowIdentifier(null, sys, table\\_types, DatabaseMetaData.bestRowTransaction, false)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint varchar(1024) int varchar(1024) int int smallint smallint\n" + "2 table_type_id 5 smallint " + (isPostDec2023 ? "15" : "16") + " 0 0 1\n"); compareResultSet(dbmd.getProcedures(null, "jdbctst", "proc"), "getProcedures(null, jdbctst, proc)", "Resultset with 9 columns\n" + "PROCEDURE_CAT PROCEDURE_SCHEM PROCEDURE_NAME Field4 Field5 Field6 REMARKS PROCEDURE_TYPE SPECIFIC_NAME\n" + "char(1) varchar(1024) varchar(256) char(1) char(1) char(1) varchar(65000) smallint varchar(10)\n" + "null jdbctst proc null null null jdbctst.proc procedure comment 1 suppressed\n"); // note that the real SPECIFIC_NAME value (containing the internal functions.id) is replaced with "suppressed" in order to get stable output compareResultSet(dbmd.getProcedureColumns(null, "jdbctst", "proc", "%%"), "getProcedureColumns(null, jdbctst, proc, %%)", "Resultset with 20 columns\n" + "PROCEDURE_CAT PROCEDURE_SCHEM PROCEDURE_NAME COLUMN_NAME COLUMN_TYPE DATA_TYPE TYPE_NAME PRECISION LENGTH SCALE RADIX NULLABLE REMARKS COLUMN_DEF SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SPECIFIC_NAME\n" + "char(1) varchar(1024) varchar(256) varchar(256) smallint int varchar(1024) int int smallint smallint smallint char(1) char(1) int int bigint int varchar(3) varchar(10)\n" + "null jdbctst proc id 1 -5 bigint 19 8 0 10 2 null null 0 0 null 1 suppressed\n" + "null jdbctst proc name 1 12 varchar 999 999 null null 2 null null 0 0 3996 2 suppressed\n" + "null jdbctst proc dt 1 91 date 0 0 null null 2 null null 0 0 null 3 suppressed\n"); // note that the real SPECIFIC_NAME value (containing the internal functions.id) is replaced with "suppressed" in order to get stable output compareResultSet(dbmd.getFunctions(null, "jdbctst", "func"), "getFunctions(null, jdbctst, func)", "Resultset with 6 columns\n" + "FUNCTION_CAT FUNCTION_SCHEM FUNCTION_NAME REMARKS FUNCTION_TYPE SPECIFIC_NAME\n" + "char(1) varchar(1024) varchar(256) varchar(65000) tinyint varchar(10)\n" + "null jdbctst func jdbctst.func function comment 2 suppressed\n"); // note that the real SPECIFIC_NAME value (containing the internal functions.id) is replaced with "suppressed" in order to get stable output compareResultSet(dbmd.getFunctionColumns(null, "jdbctst", "func", "%%"), "getFunctionColumns(null, jdbctst, func, %%)", "Resultset with 17 columns\n" + "FUNCTION_CAT FUNCTION_SCHEM FUNCTION_NAME COLUMN_NAME COLUMN_TYPE DATA_TYPE TYPE_NAME PRECISION LENGTH SCALE RADIX NULLABLE REMARKS CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SPECIFIC_NAME\n" + "char(1) varchar(1024) varchar(256) varchar(256) smallint int varchar(1024) int int smallint smallint smallint char(1) bigint int varchar(3) varchar(10)\n" + "null jdbctst func val 4 12 varchar 1022 1022 null null 2 null 4088 0 suppressed\n" + "null jdbctst func dt 3 91 date 0 0 null null 2 null null 1 suppressed\n" + "null jdbctst func id 3 4 int 10 4 0 10 2 null null 2 suppressed\n" + "null jdbctst func id 1 -5 bigint 19 8 0 10 2 null null 3 suppressed\n" + "null jdbctst func name 1 12 varchar 999 999 null null 2 null 3996 4 suppressed\n" + "null jdbctst func dt 1 91 date 0 0 null null 2 null null 5 suppressed\n"); // note that the real SPECIFIC_NAME value (containing the internal functions.id) is replaced with "suppressed" in order to get stable output compareResultSet(dbmd.getTablePrivileges(null, "sys", "table\\_types"), "getTablePrivileges(null, sys, table\\_types)", "Resultset with 7 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME GRANTOR GRANTEE PRIVILEGE IS_GRANTABLE\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(40) varchar(3)\n" + "null sys table_types monetdb public SELECT NO\n"); compareResultSet(dbmd.getColumnPrivileges(null, "sys", "table\\_types", null), "getColumnPrivileges(null, sys, table\\_types, null)", "Resultset with 8 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME GRANTOR GRANTEE PRIVILEGE IS_GRANTABLE\n" + "char(1) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(1024) varchar(40) varchar(3)\n"); if (!isPostDec2023) { compareResultSet(dbmd.getClientInfoProperties(), "getClientInfoProperties()", "Resultset with 4 columns\n" + "NAME MAX_LEN DEFAULT_VALUE DESCRIPTION\n" + "varchar(40) int varchar(128) varchar(256)\n"); } compareResultSet(dbmd.getSuperTables(null, "jdbctst", "pk_uc"), "getSuperTables(null, jdbctst, pk_uc)", "Resultset with 4 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME SUPERTABLE_NAME\n" + "char(1) " + (isPostDec2023 ? "varchar varchar varchar\n" : "char char char\n")); compareResultSet(dbmd.getPseudoColumns(null, "jdbctst", "pk_uc", "%"), "getPseudoColumns(null, jdbctst, pk_uc, %)", "Resultset with 12 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME DATA_TYPE COLUMN_SIZE DECIMAL_DIGITS NUM_PREC_RADIX COLUMN_USAGE REMARKS CHAR_OCTET_LENGTH IS_NULLABLE\n" + "char(1) " + (isPostDec2023 ? "varchar varchar varchar" : "char char char") + " int int int int " + (isPostDec2023 ? "varchar varchar int varchar\n" : "char char int char\n")); compareResultSet(dbmd.getVersionColumns(null, "jdbctst", "pk_uc"), "getVersionColumns(null, jdbctst, pk_uc)", "Resultset with 8 columns\n" + "SCOPE COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS PSEUDO_COLUMN\n" + "smallint char(1) int char(1) int int smallint smallint\n"); compareResultSet(dbmd.getSuperTypes(null, "sys", "xml"), "getSuperTypes(null, sys, xml)", "Resultset with 6 columns\n" + "TYPE_CAT TYPE_SCHEM TYPE_NAME SUPERTYPE_CAT SUPERTYPE_SCHEM SUPERTYPE_NAME\n" + "char(1) " + (isPostDec2023 ? "varchar varchar char(1) varchar varchar\n" : "char char char(1) char char\n")); compareResultSet(dbmd.getAttributes(null, "sys", "xml", "%"), "getAttributes(null, sys, xml, %)", "Resultset with 21 columns\n" + "TYPE_CAT TYPE_SCHEM TYPE_NAME ATTR_NAME DATA_TYPE ATTR_TYPE_NAME ATTR_SIZE DECIMAL_DIGITS NUM_PREC_RADIX NULLABLE REMARKS ATTR_DEF SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH ORDINAL_POSITION IS_NULLABLE SCOPE_CATALOG SCOPE_SCHEMA SCOPE_TABLE SOURCE_DATA_TYPE\n" + "char(1) " + (isPostDec2023 ? "varchar varchar varchar" : "char char char") + " int " + (isPostDec2023 ? "var" : "") + "char int int int int " + (isPostDec2023 ? "varchar varchar" : "char char") + " int int int int " + (isPostDec2023 ? "varchar(3) varchar varchar varchar" : "char(3) char char char") + " smallint\n"); sb.setLength(0); // clear the output log buffer } catch (SQLException e) { sb.setLength(0); // clear the output log buffer sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup created db objects action = "Drop"; objtype = "index"; handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc_i", "DROP INDEX jdbctst.pk_uc_i;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs_i", "DROP INDEX jdbctst.nopk_twoucs_i;"); if (testCreateDropIndexOnTmpTables) { handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc_i;", "DROP INDEX tmp.tmp_pk_uc_i;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_pk_uc_i", "DROP INDEX tmp.glbl_pk_uc_i;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_nopk_twoucs_i", "DROP INDEX tmp.tmp_nopk_twoucs_i;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_nopk_twoucs_i", "DROP INDEX tmp.glbl_nopk_twoucs_i;"); } objtype = "table"; handleExecuteDDL(stmt, action, objtype, "jdbctst.pk_uc", "DROP TABLE jdbctst.pk_uc;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_pk_uc", "DROP TABLE tmp.tmp_pk_uc;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_pk_uc", "DROP TABLE tmp.glbl_pk_uc;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.nopk_twoucs", "DROP TABLE jdbctst.nopk_twoucs;"); handleExecuteDDL(stmt, action, objtype, "tmp.tmp_nopk_twoucs", "DROP TABLE tmp.tmp_nopk_twoucs;"); handleExecuteDDL(stmt, action, objtype, "tmp.glbl_nopk_twoucs", "DROP TABLE tmp.glbl_nopk_twoucs;"); handleExecuteDDL(stmt, action, objtype, "tmp.tlargechar", "DROP TABLE tmp.tlargechar;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.\"LINES\"", "DROP TABLE jdbctst.\"LINES\";"); handleExecuteDDL(stmt, action, objtype, "jdbctst.\"ORDERS\"", "DROP TABLE jdbctst.\"ORDERS\";"); handleExecuteDDL(stmt, action, objtype, "jdbctst.\"CUSTOMERS\"", "DROP TABLE jdbctst.\"CUSTOMERS\";"); handleExecuteDDL(stmt, action, objtype, "jdbctst.fk2c", "DROP TABLE jdbctst.fk2c;"); handleExecuteDDL(stmt, action, objtype, "jdbctst.pk2c", "DROP TABLE jdbctst.pk2c;"); objtype = "procedure"; handleExecuteDDL(stmt, action, objtype, "jdbctst.proc()", "DROP PROCEDURE jdbctst.proc(bigint, varchar(999), date);"); handleExecuteDDL(stmt, action, objtype, "sys.analyze()", "COMMENT ON PROCEDURE sys.analyze() IS NULL;"); objtype = "function"; handleExecuteDDL(stmt, action, objtype, "jdbctst.func()", "DROP FUNCTION jdbctst.func(bigint, varchar(999), date);"); handleExecuteDDL(stmt, action, objtype, "sys.sin(double)", "COMMENT ON FUNCTION sys.sin(double) IS NULL;"); handleExecuteDDL(stmt, action, objtype, "sys.env()", "COMMENT ON FUNCTION sys.env() IS NULL;"); handleExecuteDDL(stmt, action, objtype, "sys.statistics()", "COMMENT ON FUNCTION sys.statistics() IS NULL;"); // All tables in schema jdbctst should now be gone, else we missed some DROP statements try { compareResultSet(dbmd.getTables(null, "jdbctst", "%%", null), "getTables(null, jdbctst, '%%', null)", "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + "char(1) varchar(1024) varchar(1024) varchar(25) varchar(1048576) char(1) char(1) char(1) char(1) char(1)\n"); sb.setLength(0); // clear the output log buffer } catch (SQLException e) { sb.setLength(0); // clear the output log buffer sb.append("FAILED: ").append(e.getMessage()).append("\n"); } handleExecuteDDL(stmt, action, "schema", "jdbctst", "SET SCHEMA sys; DROP SCHEMA jdbctst;"); closeStmtResSet(stmt, null); compareExpectedOutput("Test_DBCmetadata", ""); } private void Test_EmptySql() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { stmt = con.createStatement(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } try { stmt.execute(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { stmt.execute(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { int ret = stmt.executeUpdate(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { int ret = stmt.executeUpdate(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { long ret = stmt.executeLargeUpdate(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { long ret = stmt.executeLargeUpdate(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { stmt.addBatch(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { stmt.addBatch(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } ResultSet rs = null; try { rs = stmt.executeQuery(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { rs = stmt.executeQuery(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); PreparedStatement pstmt = null; try { pstmt = con.prepareStatement(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { pstmt = con.prepareStatement(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { pstmt = con.prepareStatement(null, Statement.RETURN_GENERATED_KEYS); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { pstmt = con.prepareStatement("", Statement.RETURN_GENERATED_KEYS); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, null); CallableStatement cstmt = null; try { pstmt = con.prepareCall(null); sb.append("Failed to check null parameter!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } try { pstmt = con.prepareCall(""); sb.append("Failed to check empty sql string!\n"); } catch (SQLException e) { sb.append(e.getMessage()).append("\n"); } closeStmtResSet(cstmt, null); compareExpectedOutput("Test_EmptySql", "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n" + "Missing SQL statement\n"); } private void Test_FetchSize() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); rs = stmt.executeQuery("SELECT * FROM _tables"); sb.append("Statement fetch size before set: ").append(stmt.getFetchSize()).append("\n"); sb.append("ResultSet fetch size before set: ").append(rs.getFetchSize()).append("\n"); stmt.setFetchSize(40); rs.setFetchSize(16384); sb.append("Statement fetch size after set: ").append(stmt.getFetchSize()).append("\n"); sb.append("ResultSet fetch size after set: ").append(rs.getFetchSize()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_FetchSize", "Statement fetch size before set: 250\n" + "ResultSet fetch size before set: 250\n" + "Statement fetch size after set: 40\n" + "ResultSet fetch size after set: 16384\n"); } private void Test_Int128() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; // check first if datatype hugeint is supported on this server boolean supportsHugeInt = false; try { stmt = con.createStatement(); // query sys.types to find out if sql datatype hugeint is supported rs = stmt.executeQuery("SELECT sqlname from sys.types where sqlname = 'hugeint';"); if (rs != null) { if (rs.next()) { if ("hugeint".equals(rs.getString(1))) supportsHugeInt = true; } rs.close(); rs = null; } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } if (!supportsHugeInt) { closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Int128", ""); return; // skip the rest of the test } // test whether we can represent a full-size int128 as JDBC results PreparedStatement insertStatement = null; try { stmt.executeUpdate("CREATE TABLE HUGEINTT (I HUGEINT)"); stmt.executeUpdate("CREATE TABLE HUGEDECT (I DECIMAL(38,19))"); BigInteger bi = new BigInteger("123456789012345678909876543210987654321"); BigDecimal bd = new BigDecimal("1234567890123456789.9876543210987654321"); insertStatement = con.prepareStatement("INSERT INTO HUGEINTT VALUES (?)"); insertStatement.setBigDecimal(1, new BigDecimal(bi)); insertStatement.executeUpdate(); insertStatement.close(); stmt.executeUpdate("INSERT INTO HUGEDECT VALUES (" + bd + ");"); rs = stmt.executeQuery("SELECT I FROM HUGEINTT"); rs.next(); BigInteger biRes = rs.getBigDecimal(1).toBigInteger(); rs.close(); sb.append("Expecting " + bi + ", got " + biRes).append("\n"); if (!bi.equals(biRes)) { sb.append("value of bi is NOT equal to biRes!\n"); } rs = stmt.executeQuery("SELECT I FROM HUGEDECT"); rs.next(); BigDecimal bdRes = rs.getBigDecimal(1); rs.close(); sb.append("Expecting " + bd + ", got " + bdRes).append("\n"); if (!bd.equals(bdRes)) { sb.append("value of bd is NOT equal to bdRes!\n"); } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup try { stmt.executeUpdate("DROP TABLE IF EXISTS HUGEINTT"); stmt.executeUpdate("DROP TABLE IF EXISTS HUGEDECT"); sb.append("SUCCESS\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(insertStatement, null); closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Int128", "Expecting 123456789012345678909876543210987654321, got 123456789012345678909876543210987654321\n" + "Expecting 1234567890123456789.9876543210987654321, got 1234567890123456789.9876543210987654321\n" + "SUCCESS\n"); } private void Test_Interval_Types() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; PreparedStatement pstmt = null; try { stmt = con.createStatement(); stmt.executeUpdate("create table Test_Interval_Types (" + "c1 interval year, c2 interval month, c3 interval day, " + "c4 interval hour, c5 interval minute, c6 interval second, " + "c7 interval year to month, c8 interval day to hour, " + "c9 interval day to minute, c10 interval day to second, " + "c11 interval hour to minute, c12 interval hour to second, " + "c13 interval minute to second)"); rs = stmt.executeQuery("select * from Test_Interval_Types"); if (rs != null) { sb.append("Showing query ResultSetMetaData\n"); ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); for (int col = 1; col <= colCount; col++) { sb.append("ColumnName: ").append(rsmd.getColumnName(col)) .append("\tColumnTypeName: ").append(rsmd.getColumnTypeName(col)) .append("\tPrecision: ").append(rsmd.getPrecision(col)) .append("\tScale: ").append(rsmd.getScale(col)) .append("\tColumnDisplaySize: ").append(rsmd.getColumnDisplaySize(col)) .append("\tColumnType: ").append(rsmd.getColumnType(col)) .append("\tColumnClassName: ").append(rsmd.getColumnClassName(col)) .append("\n"); } rs.close(); rs = null; } pstmt = con.prepareStatement("select * from Test_Interval_Types where " + "c1=? or c2=? or c3=? or c4=? or c5=? or c6=? or c7=? or c8=? or c9=? or c10=? or c11=? or c12=? or c13=?"); if (pstmt != null) { sb.append("Showing prepared query ResultSetMetaData\n"); ResultSetMetaData rsmd = pstmt.getMetaData(); int colCount = rsmd.getColumnCount(); for (int col = 1; col <= colCount; col++) { sb.append("ColumnName: ").append(rsmd.getColumnName(col)) .append("\tColumnTypeName: ").append(rsmd.getColumnTypeName(col)) .append("\tPrecision: ").append(rsmd.getPrecision(col)) .append("\tScale: ").append(rsmd.getScale(col)) .append("\tColumnDisplaySize: ").append(rsmd.getColumnDisplaySize(col)) .append("\tColumnType: ").append(rsmd.getColumnType(col)) .append("\tColumnClassName: ").append(rsmd.getColumnClassName(col)) .append("\n"); } sb.append("Showing prepared query ParameterMetaData\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); int paramCount = pmd.getParameterCount(); for (int param = 1; param <= paramCount; param++) { sb.append("ParameterTypeName: ").append(pmd.getParameterTypeName(param)) .append("\tPrecision: ").append(pmd.getPrecision(param)) .append("\tScale: ").append(pmd.getScale(param)) .append("\tParameterType: ").append(pmd.getParameterType(param)) .append("\tParameterClassName: ").append(pmd.getParameterClassName(param)) .append("\n"); } pstmt.close(); pstmt = null; } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup closeStmtResSet(pstmt, null); try { stmt.executeUpdate("drop table Test_Interval_Types"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Interval_Types", "Showing query ResultSetMetaData\n" + "ColumnName: c1 ColumnTypeName: interval year Precision: 10 Scale: 0 ColumnDisplaySize: 4 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c2 ColumnTypeName: interval month Precision: 10 Scale: 0 ColumnDisplaySize: 6 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c3 ColumnTypeName: interval day Precision: 9 Scale: 0 ColumnDisplaySize: 9 ColumnType: 2 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c4 ColumnTypeName: interval hour Precision: 11 Scale: 3 ColumnDisplaySize: 11 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c5 ColumnTypeName: interval minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c6 ColumnTypeName: interval second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c7 ColumnTypeName: interval year to month Precision: 10 Scale: 0 ColumnDisplaySize: 6 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c8 ColumnTypeName: interval day to hour Precision: 11 Scale: 3 ColumnDisplaySize: 11 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c9 ColumnTypeName: interval day to minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c10 ColumnTypeName: interval day to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c11 ColumnTypeName: interval hour to minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c12 ColumnTypeName: interval hour to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c13 ColumnTypeName: interval minute to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "Showing prepared query ResultSetMetaData\n" + "ColumnName: c1 ColumnTypeName: interval year Precision: 10 Scale: 0 ColumnDisplaySize: 4 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c2 ColumnTypeName: interval month Precision: 10 Scale: 0 ColumnDisplaySize: 6 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c3 ColumnTypeName: interval day Precision: 9 Scale: 0 ColumnDisplaySize: 9 ColumnType: 2 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c4 ColumnTypeName: interval hour Precision: 11 Scale: 3 ColumnDisplaySize: 11 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c5 ColumnTypeName: interval minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c6 ColumnTypeName: interval second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c7 ColumnTypeName: interval year to month Precision: 10 Scale: 0 ColumnDisplaySize: 6 ColumnType: 4 ColumnClassName: java.lang.Integer\n" + "ColumnName: c8 ColumnTypeName: interval day to hour Precision: 11 Scale: 3 ColumnDisplaySize: 11 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c9 ColumnTypeName: interval day to minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c10 ColumnTypeName: interval day to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c11 ColumnTypeName: interval hour to minute Precision: 13 Scale: 3 ColumnDisplaySize: 13 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c12 ColumnTypeName: interval hour to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "ColumnName: c13 ColumnTypeName: interval minute to second Precision: 15 Scale: 3 ColumnDisplaySize: 15 ColumnType: 3 ColumnClassName: java.math.BigDecimal\n" + "Showing prepared query ParameterMetaData\n" + "ParameterTypeName: interval year Precision: 10 Scale: 0 ParameterType: 4 ParameterClassName: java.lang.Integer\n" + "ParameterTypeName: interval month Precision: 10 Scale: 0 ParameterType: 4 ParameterClassName: java.lang.Integer\n" + "ParameterTypeName: interval day Precision: 9 Scale: 0 ParameterType: 2 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval hour Precision: 11 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval minute Precision: 13 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval second Precision: 15 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval year to month Precision: 10 Scale: 0 ParameterType: 4 ParameterClassName: java.lang.Integer\n" + "ParameterTypeName: interval day to hour Precision: 11 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval day to minute Precision: 13 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval day to second Precision: 15 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval hour to minute Precision: 13 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval hour to second Precision: 15 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n" + "ParameterTypeName: interval minute to second Precision: 15 Scale: 0 ParameterType: 3 ParameterClassName: java.math.BigDecimal\n"); } private void Test_PlanExplainTraceDebugCmds(boolean skipMALoutput) { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { final boolean isPreJan2022 = (dbmsMajorVersion == 11 && dbmsMinorVersion <= 41); stmt = con.createStatement(); String qry = "SELECT 1;"; rs = stmt.executeQuery(qry); while (rs.next()) { sb.append(rs.getString(1)).append("\n"); } rs.close(); rs = null; compareExpectedOutput("Test_PlanExplainTraceDebugCmds: " + qry, "1\n"); sb.setLength(0); // clear the output log buffer // plan statements are supported via JDBC qry = "plan SELECT 2;"; rs = stmt.executeQuery(qry); if (!skipMALoutput) { compareResultSet(rs, qry, ! isPreJan2022 ? "Resultset with 1 columns\n" + "rel\n" + (isPostDec2023 ? "varchar" : "clob") + "(37)\n" + "project (\n" + "| [ boolean(1) \"true\" as \"%1\".\"%1\" ]\n" + ") [ tinyint(2) \"2\" ]\n" : "Resultset with 1 columns\n" + "rel\n" + "clob(21)\n" + "project (\n" + "| [ boolean \"true\" ]\n" + ") [ tinyint \"2\" ]\n"); } rs.close(); rs = null; sb.setLength(0); // clear the output log buffer // explain statements are supported via JDBC qry = "explain SELECT 3;"; rs = stmt.executeQuery(qry); while (rs.next()) { String val = rs.getString(1); if (!val.startsWith("#")) sb.append(val).append("\n"); } rs.close(); rs = null; if (!skipMALoutput) { compareExpectedOutput("Test_PlanExplainTraceDebugCmds: " + qry, "function user.main():void;\n" + " X_1:void := querylog.define(\"explain select 3;\":str, \"default_pipe\":str, 6:int);\n" + " X_10:int := sql.resultSet(\".%2\":str, \"%2\":str, \"tinyint\":str, 2:int, 0:int, 7:int, 3:bte);\n" + "end user.main;\n"); } sb.setLength(0); // clear the output log buffer // trace statements are supported via JDBC. Note that it returns two resultsets, one with the query result and next one with the trace result. qry = "trace SELECT 4;"; rs = stmt.executeQuery(qry); while (rs.next()) { sb.append(rs.getString(1)).append("\n"); } if (stmt.getMoreResults()) { sb.append("Another resultset\n"); rs = stmt.getResultSet(); while (rs.next()) { sb.append(rs.getString(2)).append("\n"); } } rs.close(); rs = null; if (!skipMALoutput) { compareExpectedOutput("Test_PlanExplainTraceDebugCmds: " + qry, ! isPreJan2022 ? "4\n" + "Another resultset\n" + " X_1=0@0:void := querylog.define(\"trace select 4;\":str, \"default_pipe\":str, 6:int);\n" + " X_10=0:int := sql.resultSet(\".%2\":str, \"%2\":str, \"tinyint\":str, 3:int, 0:int, 7:int, 4:bte);\n" : "4\n" + "Another resultset\n" + "X_0=0@0:void := querylog.define(\"trace select 4;\":str, \"default_pipe\":str, 6:int);\n" + "X_1=0:int := sql.resultSet(\".%2\":str, \"%2\":str, \"tinyint\":str, 3:int, 0:int, 7:int, 4:bte);\n"); } sb.setLength(0); // clear the output log buffer // debug statements are NOT supported via JDBC driver, so the execution should throw an SQLException // Note: as of release 11.47 (Jun2023) the debug statement is not supported anymore, error msg: syntax error in: "debug" qry = "debug SELECT 5;"; sb.append(qry).append("\n"); rs = stmt.executeQuery(qry); // this should throw an SQLException while (rs.next()) { sb.append(rs.getString(1)).append("\n"); } rs.close(); rs = null; compareExpectedOutput("Test_PlanExplainTraceDebugCmds: " + qry, qry + "\n5\n"); sb.setLength(0); // clear the output log buffer } catch (SQLException e) { sb.append("FAILED: "); while (e != null) { sb.append(e.getMessage()).append("\n"); e = e.getNextException(); } final boolean isPreJun2023 = (dbmsMajorVersion == 11 && dbmsMinorVersion <= 45); // From Jun2023 we skip the comparison as it gives a different error msg on power8 platform: syntax error, unexpected IDENT in: "debug" if (isPreJun2023) { compareExpectedOutput("Test_PlanExplainTraceDebugCmds", "debug SELECT 5;\n" + "FAILED: SQL debugging only supported in interactive mode in: \"debug\"\n" + "Current transaction is aborted (please ROLLBACK)\n"); } } closeStmtResSet(stmt, rs); } private void Test_PSgeneratedkeys() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { con.setAutoCommit(false); // >> false: auto commit was just switched off sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate( "CREATE TABLE psgenkey (" + " id serial," + " val varchar(20)" + ")"); stmt.close(); } catch (SQLException e) { sb.append("FAILED to CREATE TABLE psgenkey: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); PreparedStatement pstmt = null; ResultSet keys = null; try { pstmt = con.prepareStatement( "INSERT INTO psgenkey (val) VALUES ('this is a test')", Statement.RETURN_GENERATED_KEYS); sb.append("1. inserting 3 records..."); pstmt.executeUpdate(); pstmt.executeUpdate(); pstmt.executeUpdate(); sb.append("success\n"); // now get the generated keys sb.append("2. getting generated keys..."); keys = pstmt.getGeneratedKeys(); if (keys == null) { sb.append("there are no keys!\n"); } else { while (keys.next()) { sb.append("generated key index: ").append(keys.getInt(1)).append("\n"); } if (keys.getStatement() == null) { sb.append("ResultSet.getStatement() should never return null!\n"); } keys.close(); } pstmt.close(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, keys); try { con.rollback(); // restore default setting con.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED to rollback: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_PSgeneratedkeys", "0. false false\n" + "1. inserting 3 records...success\n" + "2. getting generated keys...generated key index: 3\n"); } private void Test_PSgetObject() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { con.setAutoCommit(false); // >> false: auto commit was just switched off sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("1. creating test table..."); stmt.executeUpdate("CREATE TABLE table_Test_PSgetObject (ti tinyint, si smallint, i int, bi bigint)"); sb.append("success\n"); stmt.close(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); PreparedStatement pstmt = null; ResultSet rs = null; try { sb.append("2a. inserting 3 records as batch..."); pstmt = con.prepareStatement("INSERT INTO table_Test_PSgetObject (ti,si,i,bi) VALUES (?,?,?,?)"); pstmt.setShort(1, (short)1); pstmt.setShort(2, (short)1); pstmt.setInt (3, 1); pstmt.setLong(4, (long)1); pstmt.addBatch(); pstmt.setShort(1, (short)127); pstmt.setShort(2, (short)12700); pstmt.setInt (3, 1270000); pstmt.setLong(4, (long)127000000); pstmt.addBatch(); pstmt.setShort(1, (short)-127); pstmt.setShort(2, (short)-12700); pstmt.setInt (3, -1270000); pstmt.setLong(4, (long)-127000000); pstmt.addBatch(); pstmt.executeBatch(); sb.append(" passed\n"); sb.append(pstmt.toString()); // test showing prepared statement sb.append("2b. closing PreparedStatement..."); pstmt.close(); sb.append(" passed\n"); } catch (SQLException e) { sb.append("FAILED to INSERT data: ").append(e.getMessage()).append("\n"); while ((e = e.getNextException()) != null) sb.append("FAILED: ").append(e.getMessage()).append("\n"); } try { sb.append("3a. selecting records..."); pstmt = con.prepareStatement("SELECT ti,si,i,bi FROM table_Test_PSgetObject ORDER BY ti,si,i,bi"); rs = pstmt.executeQuery(); sb.append(" passed\n"); while (rs.next()) { // test fix for https://www.monetdb.org/bugzilla/show_bug.cgi?id=4026 Short ti = (Short) rs.getObject(1); Short si = (Short) rs.getObject(2); Integer i = (Integer) rs.getObject(3); Long bi = (Long) rs.getObject(4); sb.append(" Retrieved row data: ti=").append(ti).append(" si=").append(si).append(" i=").append(i).append(" bi=").append(bi).append("\n"); } sb.append("3b. closing ResultSet..."); rs.close(); sb.append(" passed\n"); sb.append("3c. closing PreparedStatement..."); pstmt.close(); sb.append(" passed\n"); } catch (SQLException e) { sb.append("FAILED to RETRIEVE data: ").append(e.getMessage()).append("\n"); while ((e = e.getNextException()) != null) sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); try { sb.append("4. Rollback changes..."); con.rollback(); sb.append(" passed\n"); // restore default setting con.setAutoCommit(true); } catch (SQLException e) { sb.append("FAILED to rollback: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_PSgetObject", "0. false false\n" + "1. creating test table...success\n" + "2a. inserting 3 records as batch... passed\n" + "Prepared SQL: INSERT INTO table_Test_PSgetObject (ti,si,i,bi) VALUES (?,?,?,?)\n" + " parameter 1 tinyint, set value: -127\n" + " parameter 2 smallint, set value: -12700\n" + " parameter 3 int, set value: -1270000\n" + " parameter 4 bigint, set value: -127000000\n" + "2b. closing PreparedStatement... passed\n" + "3a. selecting records... passed\n" + " Retrieved row data: ti=-127 si=-12700 i=-1270000 bi=-127000000\n" + " Retrieved row data: ti=1 si=1 i=1 bi=1\n" + " Retrieved row data: ti=127 si=12700 i=1270000 bi=127000000\n" + "3b. closing ResultSet... passed\n" + "3c. closing PreparedStatement... passed\n" + "4. Rollback changes... passed\n"); } private void Test_PSlargebatchval() { sb.setLength(0); // clear the output log buffer byte[] errorBytes = new byte[] { (byte) 0xe2, (byte) 0x80, (byte) 0xa7 }; String errorStr = new String(errorBytes, StandardCharsets.UTF_8); StringBuilder repeatedErrorStr = new StringBuilder(); for (int i = 0; i < 8170;i++) { repeatedErrorStr.append(errorStr); } String largeStr = repeatedErrorStr.toString(); Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { // >> true: auto commit should be on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("1. creating test table..."); stmt.execute("CREATE TABLE Test_PSlargebatchval (c INT, a CLOB, b DOUBLE)"); sb.append("success\n"); sb.append("2. prepare insert..."); pstmt = con.prepareStatement("INSERT INTO Test_PSlargebatchval VALUES (?,?,?)"); sb.append("success\n"); pstmt.setLong(1, 1L); pstmt.setString(2, largeStr); pstmt.setDouble(3, 1.0); pstmt.addBatch(); pstmt.executeBatch(); sb.append("3. inserted 1 large string\n"); /* test issue reported at https://www.monetdb.org/bugzilla/show_bug.cgi?id=3470 */ pstmt.setLong(1, -2L); pstmt.setClob(2, new StringReader(largeStr)); pstmt.setDouble(3, -2.0); pstmt.addBatch(); pstmt.executeBatch(); sb.append("4. inserted 1 large clob via StringReader() object\n"); Clob myClob = con.createClob(); myClob.setString(1L, largeStr); pstmt.setLong(1, 123456789L); pstmt.setClob(2, myClob); pstmt.setDouble(3, 12345678901.98765); pstmt.addBatch(); pstmt.executeBatch(); sb.append("5. inserted 1 large clob via createClob() object\n"); pstmt.close(); sb.append("6. select count(*)... "); rs = stmt.executeQuery("SELECT COUNT(*) FROM Test_PSlargebatchval"); if (rs.next()) sb.append(rs.getInt(1)).append(" rows inserted.\n"); rs.close(); sb.append("7. drop table..."); stmt.execute("DROP TABLE Test_PSlargebatchval"); sb.append("success\n"); stmt.close(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); while ((e = e.getNextException()) != null) sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); closeStmtResSet(pstmt, null); compareExpectedOutput("Test_PSlargebatchval", "0. true true\n" + "1. creating test table...success\n" + "2. prepare insert...success\n" + "3. inserted 1 large string\n" + "4. inserted 1 large clob via StringReader() object\n" + "5. inserted 1 large clob via createClob() object\n" + "6. select count(*)... 3 rows inserted.\n" + "7. drop table...success\n"); } private void Test_PSlargeresponse(String conURL) { sb.setLength(0); // clear the output log buffer PreparedStatement pstmt = null; try { sb.append("1. DatabaseMetadata environment retrieval... "); // retrieve this to simulate a bug report con.getMetaData().getURL(); // There used to be a test "if (conURL.startsWith(dbmdURL))" here // but with the new URLs that is too simplistic. sb.append("oke").append("\n"); pstmt = con.prepareStatement("select * from columns"); sb.append("2. empty call..."); // should succeed (no arguments given) pstmt.execute(); sb.append(" passed\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, null); compareExpectedOutput("Test_PSlargeresponse", "1. DatabaseMetadata environment retrieval... oke\n" + "2. empty call... passed\n"); } private void Test_PSmanycon(String arg0) { sb.setLength(0); // clear the output log buffer final int maxCons = 60; // default max_clients is 64, 2 connections are already open from this program List<PreparedStatement> pss = new ArrayList<PreparedStatement>(maxCons); // PreparedStatements go in here try { // spawn a lot of Connections with 1 PreparedStatement, just for fun... int i = 1; sb.append("Establishing Connection "); for (; i <= maxCons; i++) { sb.append(i); Connection conx = DriverManager.getConnection(arg0); sb.append(","); // do something with the connection to test if it works PreparedStatement pstmt = conx.prepareStatement("select " + i); sb.append(" "); pss.add(pstmt); } sb.append("\n"); // now try to nicely execute them i = 1; sb.append("Executing PreparedStatement\n"); for (Iterator<PreparedStatement> it = pss.iterator(); it.hasNext(); i++) { PreparedStatement pstmt = it.next(); // see if the connection still works sb.append(i).append("..."); if (!pstmt.execute()) sb.append("should have seen a ResultSet!"); ResultSet rs = pstmt.getResultSet(); if (rs != null) { if (!rs.next()) sb.append("ResultSet is empty"); sb.append(" result: ").append(rs.getString(1)); } // close the connection and associated resources pstmt.getConnection().close(); sb.append(", closed. "); if (i % 5 == 0) { // inject a failed transaction Connection conZZ = DriverManager.getConnection(arg0); Statement stmt = con.createStatement(); try { int affrows = stmt.executeUpdate("update foo where bar is wrong"); sb.append("oops, faulty statement just got through"); } catch (SQLException e) { sb.append("Forced transaction failure"); } sb.append("\n"); closeStmtResSet(stmt, null); conZZ.close(); } } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_PSmanycon", "Establishing Connection 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, " + "11, 12, 13, 14, 15, 16, 17, 18, 19, 20, " + "21, 22, 23, 24, 25, 26, 27, 28, 29, 30, " + "31, 32, 33, 34, 35, 36, 37, 38, 39, 40, " + "41, 42, 43, 44, 45, 46, 47, 48, 49, 50, " + "51, 52, 53, 54, 55, 56, 57, 58, 59, 60, \n" + "Executing PreparedStatement\n" + "1... result: 1, closed. 2... result: 2, closed. 3... result: 3, closed. 4... result: 4, closed. 5... result: 5, closed. Forced transaction failure\n" + "6... result: 6, closed. 7... result: 7, closed. 8... result: 8, closed. 9... result: 9, closed. 10... result: 10, closed. Forced transaction failure\n" + "11... result: 11, closed. 12... result: 12, closed. 13... result: 13, closed. 14... result: 14, closed. 15... result: 15, closed. Forced transaction failure\n" + "16... result: 16, closed. 17... result: 17, closed. 18... result: 18, closed. 19... result: 19, closed. 20... result: 20, closed. Forced transaction failure\n" + "21... result: 21, closed. 22... result: 22, closed. 23... result: 23, closed. 24... result: 24, closed. 25... result: 25, closed. Forced transaction failure\n" + "26... result: 26, closed. 27... result: 27, closed. 28... result: 28, closed. 29... result: 29, closed. 30... result: 30, closed. Forced transaction failure\n" + "31... result: 31, closed. 32... result: 32, closed. 33... result: 33, closed. 34... result: 34, closed. 35... result: 35, closed. Forced transaction failure\n" + "36... result: 36, closed. 37... result: 37, closed. 38... result: 38, closed. 39... result: 39, closed. 40... result: 40, closed. Forced transaction failure\n" + "41... result: 41, closed. 42... result: 42, closed. 43... result: 43, closed. 44... result: 44, closed. 45... result: 45, closed. Forced transaction failure\n" + "46... result: 46, closed. 47... result: 47, closed. 48... result: 48, closed. 49... result: 49, closed. 50... result: 50, closed. Forced transaction failure\n" + "51... result: 51, closed. 52... result: 52, closed. 53... result: 53, closed. 54... result: 54, closed. 55... result: 55, closed. Forced transaction failure\n" + "56... result: 56, closed. 57... result: 57, closed. 58... result: 58, closed. 59... result: 59, closed. 60... result: 60, closed. Forced transaction failure\n"); } private void Test_PSmetadata() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { con.setAutoCommit(false); // >> false: auto commit was just switched off sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = 0; updates = stmt.executeUpdate("CREATE TABLE table_Test_PSmetadata ( myint int, mydouble double, mybool boolean, myvarchar varchar(15), myclob clob )"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); // all NULLs updates = stmt.executeUpdate("INSERT INTO table_Test_PSmetadata VALUES (NULL, NULL, NULL, NULL, NULL)"); if (updates != 1) sb.append("2a. Expected 1 got ").append(updates).append(" instead\n"); // all filled in updates = stmt.executeUpdate("INSERT INTO table_Test_PSmetadata VALUES (2 , 3.0, true, 'A string', 'bla bla bla')"); if (updates != 1) sb.append("2b. Expected 1 got ").append(updates).append(" instead\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); PreparedStatement pstmt = null; try { pstmt = con.prepareStatement("SELECT CASE WHEN myint IS NULL THEN 0 ELSE 1 END AS intnull, * FROM table_Test_PSmetadata WHERE myint = ?"); sb.append(pstmt.toString()); // test showing prepared statement // testing and showing result set meta data ResultSetMetaData rsmd = pstmt.getMetaData(); sb.append("rsmd. ").append(rsmd.getColumnCount()).append(" columns:\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append("RCol ").append(col).append("\n"); sb.append(" classname ").append(rsmd.getColumnClassName(col)).append("\n"); sb.append(" displaysize ").append(rsmd.getColumnDisplaySize(col)).append("\n"); sb.append(" label ").append(rsmd.getColumnLabel(col)).append("\n"); sb.append(" name ").append(rsmd.getColumnName(col)).append("\n"); sb.append(" type ").append(rsmd.getColumnType(col)).append("\n"); sb.append(" typename ").append(rsmd.getColumnTypeName(col)).append("\n"); sb.append(" precision ").append(rsmd.getPrecision(col)).append("\n"); sb.append(" scale ").append(rsmd.getScale(col)).append("\n"); sb.append(" catalogname ").append(rsmd.getCatalogName(col)).append("\n"); sb.append(" schemaname ").append(rsmd.getSchemaName(col)).append("\n"); sb.append(" tablename ").append(rsmd.getTableName(col)).append("\n"); sb.append(" autoincrement ").append(rsmd.isAutoIncrement(col)).append("\n"); sb.append(" casesensitive ").append(rsmd.isCaseSensitive(col)).append("\n"); sb.append(" currency ").append(rsmd.isCurrency(col)).append("\n"); sb.append(" defwritable ").append(rsmd.isDefinitelyWritable(col)).append("\n"); sb.append(" nullable ").append(rsmd.isNullable(col)).append("\n"); sb.append(" readonly ").append(rsmd.isReadOnly(col)).append("\n"); sb.append(" searchable ").append(rsmd.isSearchable(col)).append("\n"); sb.append(" signed ").append(rsmd.isSigned(col)).append("\n"); sb.append(" writable ").append(rsmd.isWritable(col)).append("\n"); } showParams(pstmt); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, null); compareExpectedOutput("Test_PSmetadata", "0. false\tfalse\n" + "Prepared SQL: SELECT CASE WHEN myint IS NULL THEN 0 ELSE 1 END AS intnull, * FROM table_Test_PSmetadata WHERE myint = ?\n" + " parameter 1 int, set value: <null>\n" + "rsmd. 6 columns:\n" + "RCol 1\n" + " classname java.lang.Short\n" + " displaysize 3\n" + " label intnull\n" + " name intnull\n" + " type -6\n" + " typename tinyint\n" + " precision 3\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename \n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "RCol 2\n" + " classname java.lang.Integer\n" + " displaysize 10\n" + " label myint\n" + " name myint\n" + " type 4\n" + " typename int\n" + " precision 10\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename table_test_psmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "RCol 3\n" + " classname java.lang.Double\n" + " displaysize 15\n" + " label mydouble\n" + " name mydouble\n" + " type 8\n" + " typename double\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename table_test_psmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "RCol 4\n" + " classname java.lang.Boolean\n" + " displaysize 5\n" + " label mybool\n" + " name mybool\n" + " type 16\n" + " typename boolean\n" + " precision 1\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename table_test_psmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "RCol 5\n" + " classname java.lang.String\n" + " displaysize 15\n" + " label myvarchar\n" + " name myvarchar\n" + " type 12\n" + " typename varchar\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename table_test_psmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "RCol 6\n" + " classname java.lang.String\n" + " displaysize 0\n" + " label myclob\n" + " name myclob\n" + " type 12\n" + " typename " + (isPostDec2023 ? "varchar" : "clob") + "\n" + " precision 0\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + " tablename table_test_psmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 2\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "pmd. 1 parameters:\n" + "Param 1\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + " precision 10\n" + " scale 0\n" + " type 4\n" + " typename int\n" + " classname java.lang.Integer\n" + " mode 1 (IN)\n" + "0. true\ttrue\n"); } private void Test_PSsetBytes() { sb.setLength(0); // clear the output log buffer PreparedStatement pstmt = null; ResultSet rs = null; try { sb.append("1 Create table\n"); pstmt = con.prepareStatement("create table t7346(col1 clob, col2 blob)"); sb.append(" pstmt has ") .append(pstmt.getMetaData().getColumnCount()) .append(" result columns and ") .append(pstmt.getParameterMetaData().getParameterCount()) .append(" parameters\n"); pstmt.execute(); pstmt.close(); sb.append("2 Prepare Insert data\n"); pstmt = con.prepareStatement("insert into t7346 (col1,col2) values(?,?)"); sb.append(" pstmt has ") .append(pstmt.getMetaData().getColumnCount()) .append(" result columns and ") .append(pstmt.getParameterMetaData().getParameterCount()) .append(" parameters\n"); String val = "0123456789abcdef"; pstmt.setString(1, val); pstmt.setBytes(2, val.getBytes(StandardCharsets.UTF_8)); sb.append("3 Insert data row 1\n"); pstmt.execute(); val = "~!@#$%^&*()_+`1-=][{}\\|';:,<.>/?"; pstmt.setString(1, val); pstmt.setBytes(2, val.getBytes(StandardCharsets.UTF_8)); sb.append("4 Insert data row 2\n"); pstmt.execute(); val = "\u00e0\u004f\u20f0\u0020\u00ea\u003a\u0069\u0010\u00a2\u00d8\u0008\u0001\u002b\u0030\u019c\u129e"; pstmt.setString(1, val); pstmt.setBytes(2, val.getBytes(StandardCharsets.UTF_8)); sb.append("4 Insert data row 3\n"); pstmt.execute(); pstmt.close(); sb.append("5 Prepare Select data\n"); pstmt = con.prepareStatement("select col1, length(col1) as len_col1, col2, length(col2) as len_col2 from t7346"); sb.append(" pstmt has ") .append(pstmt.getMetaData().getColumnCount()) .append(" result columns and ") .append(pstmt.getParameterMetaData().getParameterCount()) .append(" parameters\n"); sb.append("6 Execute Select\n"); rs = pstmt.executeQuery(); if (rs != null) { sb.append(" rs has ") .append(rs.getMetaData().getColumnCount()) .append(" result columns\n"); sb.append("7 Show data rows\n"); for (int c = 1; c <= rs.getMetaData().getColumnCount(); c++) { if (c > 1) sb.append("\t"); sb.append(rs.getMetaData().getColumnName(c)); } sb.append("\n"); while (rs.next()) { sb.append(rs.getString("col1")).append("\t") .append(rs.getInt("len_col1")).append("\t") .append(byteArrayToHexStr(rs.getBytes("col2"))).append("\t") .append(rs.getString("col2")).append("\t") .append(rs.getInt("len_col2")).append("\n"); } rs.close(); rs = null; } pstmt.close(); pstmt = null; } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); // cleanup created test table try { sb.append("8 Drop table\n"); pstmt = con.prepareStatement("drop table if exists t7346"); sb.append(" pstmt has ") .append(pstmt.getMetaData().getColumnCount()) .append(" result columns and ") .append(pstmt.getParameterMetaData().getParameterCount()) .append(" parameters\n"); pstmt.execute(); pstmt.close(); pstmt = null; } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); compareExpectedOutput("Test_PSsetBytes", "1 Create table\n" + " pstmt has 0 result columns and 0 parameters\n" + "2 Prepare Insert data\n" + " pstmt has 0 result columns and 2 parameters\n" + "3 Insert data row 1\n" + "4 Insert data row 2\n" + "4 Insert data row 3\n" + "5 Prepare Select data\n" + " pstmt has 4 result columns and 0 parameters\n" + "6 Execute Select\n" + " rs has 4 result columns\n" + "7 Show data rows\n" + "col1 len_col1 col2 len_col2\n" + "0123456789abcdef 16 30313233343536373839616263646566 30313233343536373839616263646566 16\n" + "~!@#$%^&*()_+`1-=][{}|';:,<.>/? 31 7E21402324255E262A28295F2B60312D3D5D5B7B7D5C7C273B3A2C3C2E3E2F3F 7E21402324255E262A28295F2B60312D3D5D5B7B7D5C7C273B3A2C3C2E3E2F3F 32\n" + "\u00e0\u004f\u20f0\u0020\u00ea\u003a\u0069\u0010\u00a2\u00d8\u0008\u0001\u002b\u0030\u019c\u129e 16 C3A04FE283B020C3AA3A6910C2A2C39808012B30C69CE18A9E C3A04FE283B020C3AA3A6910C2A2C39808012B30C69CE18A9E 25\n" + "8 Drop table\n" + " pstmt has 0 result columns and 0 parameters\n"); } static private final char[] HEXES = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; private String byteArrayToHexStr(final byte[] bytes) { if (bytes == null) return null; final int len = bytes.length; final StringBuilder buf = new StringBuilder(len * 2); // convert the bytes into hex codes for (int i = 0; i < len; i++) { byte b = bytes[i]; buf.append(HEXES[(b & 0xF0) >> 4]) .append(HEXES[(b & 0x0F)]); } return buf.toString(); } private void Test_PSsomeamount() { sb.setLength(0); // clear the output log buffer PreparedStatement pstmt = null; ResultSet rs = null; try { // >> true: auto commit should be on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); sb.append("1. Preparing and executing a unique statement\n"); for (int i = 0; i < 120; i++) { pstmt = con.prepareStatement("select " + i + ", " + i + " = ?"); pstmt.setInt(1, i); rs = pstmt.executeQuery(); if (rs != null && rs.next() && i % 20 == 0) { sb.append(rs.getInt(1)).append(", ").append(rs.getBoolean(2)).append("\n"); } /* next call should cause resources on the server to be freed */ pstmt.close(); } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); compareExpectedOutput("Test_PSsomeamount", "0. true true\n" + "1. Preparing and executing a unique statement\n" + "0, true\n" + "20, true\n" + "40, true\n" + "60, true\n" + "80, true\n" + "100, true\n"); } /* Create a lot of PreparedStatements, to emulate webloads such as those from Hibernate. */ /* this test is same as Test_PSsomeamount() but for many more PreparedStatements to stress the server */ private void Test_PSlargeamount() { sb.setLength(0); // clear the output log buffer PreparedStatement pstmt = null; ResultSet rs = null; try { // >> true: auto commit should be on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); sb.append("1. Preparing and executing a unique statement\n"); for (int i = 0; i < 50001; i++) { pstmt = con.prepareStatement("select " + i + ", " + i + " = ?"); pstmt.setInt(1, i); rs = pstmt.executeQuery(); if (rs != null && rs.next() && i % 1000 == 0) { sb.append(rs.getInt(1)).append(", ").append(rs.getBoolean(2)).append("\n"); } /* next call should cause resources on the server to be freed */ pstmt.close(); } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); compareExpectedOutput("Test_PSlargeamount", "0. true true\n" + "1. Preparing and executing a unique statement\n" + "0, true\n" + "1000, true\n" + "2000, true\n" + "3000, true\n" + "4000, true\n" + "5000, true\n" + "6000, true\n" + "7000, true\n" + "8000, true\n" + "9000, true\n" + "10000, true\n" + "11000, true\n" + "12000, true\n" + "13000, true\n" + "14000, true\n" + "15000, true\n" + "16000, true\n" + "17000, true\n" + "18000, true\n" + "19000, true\n" + "20000, true\n" + "21000, true\n" + "22000, true\n" + "23000, true\n" + "24000, true\n" + "25000, true\n" + "26000, true\n" + "27000, true\n" + "28000, true\n" + "29000, true\n" + "30000, true\n" + "31000, true\n" + "32000, true\n" + "33000, true\n" + "34000, true\n" + "35000, true\n" + "36000, true\n" + "37000, true\n" + "38000, true\n" + "39000, true\n" + "40000, true\n" + "41000, true\n" + "42000, true\n" + "43000, true\n" + "44000, true\n" + "45000, true\n" + "46000, true\n" + "47000, true\n" + "48000, true\n" + "49000, true\n" + "50000, true\n"); } private void Test_PSsqldata() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = stmt.executeUpdate("CREATE TABLE table_Test_PSsqldata ( myinet inet, myurl url )"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); pstmt = con.prepareStatement("INSERT INTO table_Test_PSsqldata VALUES (?, ?)"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append(pmd.getParameterCount()).append(" parameters:\n"); for (int parm = 1; parm <= pmd.getParameterCount(); parm++) { sb.append("Parm ").append(parm).append("\n"); sb.append(" type ").append(pmd.getParameterType(parm)).append("\n"); sb.append(" typename ").append(pmd.getParameterTypeName(parm)).append("\n"); sb.append(" classname ").append(pmd.getParameterClassName(parm)).append("\n"); } INET tinet = new INET(); tinet.fromString("172.5.5.5/24"); URL turl = new URL(); try { turl.fromString("http://www.monetdb.org/"); } catch (Exception e) { sb.append("conversion failed: ").append(e.getMessage()).append("\n"); } pstmt.setObject(1, tinet); pstmt.setObject(2, turl); // insert first record pstmt.execute(); sb.append(pstmt.toString()); // test showing prepared statement try { tinet.setNetmaskBits(16); } catch (Exception e) { sb.append("setNetmaskBits failed: ").append(e.getMessage()).append("\n"); } // insert second record pstmt.execute(); rs = stmt.executeQuery("SELECT * FROM table_Test_PSsqldata"); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1; rs.next(); i++) { for (int col = 1; col <= rsmd.getColumnCount(); col++) { Object x = rs.getObject(col); if (x == null || rs.wasNull()) { sb.append(i).append(".\t<null>\n"); } else { sb.append(i).append(".\t").append(x.toString()).append("\n"); if (x instanceof INET) { INET inet = (INET)x; sb.append(" ").append(inet.getAddress()).append("/").append(inet.getNetmaskBits()).append("\n"); sb.append(" ").append(inet.getInetAddress().toString()).append("\n"); } else if (x instanceof URL) { URL url = (URL)x; sb.append(" ").append(url.getURL().toString()).append("\n"); } } } } con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); closeStmtResSet(pstmt, null); compareExpectedOutput("Test_PSsqldata", "0. false false\n" + "2 parameters:\n" + "Parm 1\n" + " type 12\n" + " typename inet\n" + " classname org.monetdb.jdbc.types.INET\n" + "Parm 2\n" + " type 12\n" + " typename url\n" + " classname org.monetdb.jdbc.types.URL\n" + "Prepared SQL: INSERT INTO table_Test_PSsqldata VALUES (?, ?)\n" + " parameter 1 inet, set value: inet '172.5.5.5/24'\n" + " parameter 2 url, set value: url 'http://www.monetdb.org/'\n" + "1. 172.5.5.5/24\n" + " 172.5.5.5/24\n" + " /172.5.5.5\n" + "1. http://www.monetdb.org/\n" + " http://www.monetdb.org/\n" + "2. 172.5.5.5/24\n" + " 172.5.5.5/24\n" + " /172.5.5.5\n" + "2. http://www.monetdb.org/\n" + " http://www.monetdb.org/\n" + "0. true true\n"); } private void Test_PStimedate() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = stmt.executeUpdate("CREATE TABLE Test_PStimedate (t time, ts timestamp, d date)"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); pstmt = con.prepareStatement("INSERT INTO Test_PStimedate VALUES (?, ?, ?)"); sb.append("1. empty call..."); try { // should fail (as no parameters set) pstmt.execute(); sb.append(" UNexpected PASS!\n"); } catch (SQLException e) { sb.append(" expected exception\n"); } sb.append("2. inserting a record..."); final java.util.Date d = new java.util.Date(); final long tm = d.getTime(); pstmt.setTime(1, new java.sql.Time(tm)); pstmt.setTimestamp(2, new java.sql.Timestamp(tm)); pstmt.setDate(3, new java.sql.Date(tm)); pstmt.executeUpdate(); sb.append(" passed\n"); sb.append("3. closing PreparedStatement..."); pstmt.close(); sb.append(" passed\n"); sb.append("4. selecting record..."); pstmt = con.prepareStatement("SELECT * FROM Test_PStimedate"); rs = pstmt.executeQuery(); sb.append(" passed\n"); while (rs != null && rs.next()) { for (int j = 1; j <= 3; j++) { sb.append((j+4)).append(". retrieving..."); Object x = rs.getObject(j); boolean matches = false; if (x instanceof java.sql.Time) { sb.append(" (Time)"); matches = (new java.sql.Time(tm)).toString().equals(x.toString()); } else if (x instanceof java.sql.Date) { sb.append(" (Date)"); matches = (new java.sql.Date(tm)).toString().equals(x.toString()); } else if (x instanceof java.sql.Timestamp) { sb.append(" (Timestamp)"); matches = (new java.sql.Timestamp(tm)).toString().equals(x.toString()); } if (matches) { sb.append(" passed\n"); } else { sb.append(" FAILED (").append(x).append(" is not ").append(d).append(")\n"); } } } con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); closeStmtResSet(pstmt, rs); compareExpectedOutput("Test_PStimedate", "0. false false\n" + "1. empty call... expected exception\n" + "2. inserting a record... passed\n" + "3. closing PreparedStatement... passed\n" + "4. selecting record... passed\n" + "5. retrieving... (Time) passed\n" + "6. retrieving... (Timestamp) passed\n" + "7. retrieving... (Date) passed\n" + "0. true true\n"); } private void Test_PStimezone() { sb.setLength(0); // clear the output log buffer // make sure this test is reproducable regardless timezone // setting, by overriding the VM's default // we have to make sure that one doesn't have daylight // savings corrections TimeZone.setDefault(TimeZone.getTimeZone("UTC")); Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = stmt.executeUpdate("CREATE TABLE Test_PStimezone (ts timestamp, tsz timestamp with time zone, t time, tz time with time zone)"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); pstmt = con.prepareStatement("INSERT INTO Test_PStimezone VALUES (?, ?, ?, ?)"); sb.append("1. empty call..."); try { // should fail (as no parameters set) pstmt.execute(); sb.append(" UNexpected PASS!\n"); } catch (SQLException e) { sb.append(" expected exception\n"); } sb.append("2. inserting records...\n"); java.sql.Timestamp ts = new java.sql.Timestamp(0L); java.sql.Time t = new java.sql.Time(0L); Calendar c = Calendar.getInstance(); SimpleDateFormat tsz = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); SimpleDateFormat tz = new SimpleDateFormat("HH:mm:ss.SSSZ"); tsz.setTimeZone(c.getTimeZone()); tz.setTimeZone(tsz.getTimeZone()); sb.append("inserting (").append(c.getTimeZone().getID()).append(") ").append(tsz.format(ts)).append(", ").append(tz.format(t)).append("\n"); pstmt.setTimestamp(1, ts); pstmt.setTimestamp(2, ts); pstmt.setTime(3, t); pstmt.setTime(4, t); pstmt.executeUpdate(); c.setTimeZone(TimeZone.getTimeZone("UTC")); sb.append("inserting with calendar timezone ").append(c.getTimeZone().getID()).append("\n"); pstmt.setTimestamp(1, ts, c); pstmt.setTimestamp(2, ts, c); pstmt.setTime(3, t, c); pstmt.setTime(4, t, c); pstmt.executeUpdate(); c.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); sb.append("inserting with calendar timezone ").append(c.getTimeZone().getID()).append("\n"); pstmt.setTimestamp(1, ts, c); pstmt.setTimestamp(2, ts); pstmt.setTime(3, t, c); pstmt.setTime(4, t); pstmt.executeUpdate(); c.setTimeZone(TimeZone.getTimeZone("GMT+04:15")); sb.append("inserting with calendar timezone ").append(c.getTimeZone().getID()).append("\n"); pstmt.setTimestamp(1, ts); pstmt.setTimestamp(2, ts, c); pstmt.setTime(3, t); pstmt.setTime(4, t, c); pstmt.executeUpdate(); sb.append(" done\n"); sb.append("3. closing PreparedStatement..."); pstmt.close(); sb.append(" passed\n"); sb.append("4. selecting records..."); pstmt = con.prepareStatement("SELECT * FROM Test_PStimezone"); rs = pstmt.executeQuery(); sb.append(" passed\n"); // The tz fields should basically always be the same // (exactly 1st Jan 1970) since whatever timezone is used, // the server retains it, and Java restores it. // The zoneless fields will show differences since the time // is inserted translated to the given timezones, and // retrieved as in they were given in those timezones. // When the insert zone matches the retrieve zone, Java should // eventually see 1st Jan 1970. while (rs.next()) { sb.append("retrieved row (String):\n") .append(rs.getString("ts")).append(" | ") // .append(rs.getString("tsz")).append(" | ") -- this values changes when summer or wintertime changes so no stable output .append(rs.getString("t")).append(" | ") // .append(rs.getString("tz")) -- this values changes when summer or wintertime changes so no stable output .append("\n"); rs.getString("tsz"); rs.getString("tz"); tsz.setTimeZone(TimeZone.getDefault()); tz.setTimeZone(tsz.getTimeZone()); sb.append("default (").append(tsz.getTimeZone().getID()).append("):\n") .append(tsz.format(rs.getTimestamp("ts"))).append(" | ") .append(tsz.format(rs.getTimestamp("tsz"))).append(" | ") .append(tz.format(rs.getTime("t"))).append(" | ") .append(tz.format(rs.getTime("tz"))).append("\n"); c.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); sb.append(c.getTimeZone().getID()).append(":\n") .append(rs.getTimestamp("ts", c)).append(" | ") .append(rs.getTimestamp("tsz", c)).append(" | ") .append(rs.getTime("t", c)).append(" | ") .append(rs.getTime("tz", c)).append("\n"); c.setTimeZone(TimeZone.getTimeZone("Africa/Windhoek")); sb.append(c.getTimeZone().getID()).append(":\n") .append(rs.getTimestamp("ts", c)).append(" | ") .append(rs.getTimestamp("tsz", c)).append(" | ") .append(rs.getTime("t", c)).append(" | ") .append(rs.getTime("tz", c)).append("\n"); sb.append("getObject:\n") .append(rs.getObject("ts")).append(" | ") // .append(rs.getObject("tsz")).append(" | ") -- this value changes on different time zones, so no stable output .append(rs.getObject("t")).append(" | ") // .append(rs.getObject("tz")) -- this value changes on different time zones, so no stable output .append("\n"); rs.getObject("tsz"); rs.getObject("tz"); SQLWarning w = rs.getWarnings(); while (w != null) { sb.append(w.getMessage()).append("\n"); w = w.getNextWarning(); } } con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); closeStmtResSet(pstmt, rs); compareExpectedOutput("Test_PStimezone", "0. false false\n" + "1. empty call... expected exception\n" + "2. inserting records...\n" + "inserting (UTC) 1970-01-01 00:00:00.000+0000, 00:00:00.000+0000\n" + "inserting with calendar timezone UTC\n" + "inserting with calendar timezone America/Los_Angeles\n" + "inserting with calendar timezone GMT+04:15\n" + " done\n" + "3. closing PreparedStatement... passed\n" + "4. selecting records... passed\n" + "retrieved row (String):\n" + // old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + "1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" + "Africa/Windhoek:\n" + "1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" + "getObject:\n" + // old output "1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" + "1970-01-01 00:00:00.0 | 00:00:00 | \n" + "retrieved row (String):\n" + // old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + "1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" + "Africa/Windhoek:\n" + "1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" + "getObject:\n" + // old output "1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" + "1970-01-01 00:00:00.0 | 00:00:00 | \n" + "retrieved row (String):\n" + // old output "1969-12-31 16:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 16:00:00 | 01:00:00+01:00\n" + "1969-12-31 16:00:00.000000 | 16:00:00 | \n" + "default (UTC):\n" + "1969-12-31 16:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 16:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + "1970-01-01 00:00:00.0 | 1970-01-01 00:00:00.0 | 00:00:00 | 00:00:00\n" + "Africa/Windhoek:\n" + "1969-12-31 14:00:00.0 | 1970-01-01 00:00:00.0 | 14:00:00 | 00:00:00\n" + "getObject:\n" + // old output "1969-12-31 16:00:00.0 | 1970-01-01T01:00+01:00 | 16:00:00 | 01:00+01:00\n" + "1969-12-31 16:00:00.0 | 16:00:00 | \n" + "retrieved row (String):\n" + // old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + "1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" + "Africa/Windhoek:\n" + "1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" + "getObject:\n" + // old output "1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" + "1970-01-01 00:00:00.0 | 00:00:00 | \n" + "0. true true\n"); } private void Test_PStypes() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = stmt.executeUpdate( "CREATE TABLE htmtest (" + " htmid bigint NOT NULL," + " ra double ," + " decl double ," + " dra double ," + " ddecl double ," + " flux double ," + " dflux double ," + " freq double ," + " bw double ," + " type decimal(1,0)," + " imageurl url(100)," + " comment varchar(100)," + " CONSTRAINT htmtest_htmid_pkey PRIMARY KEY (htmid)" + ")" ); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); // index is not used, but the original bug had it too updates = stmt.executeUpdate("CREATE INDEX htmid ON htmtest (htmid)"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1. Expected -2 got ").append(updates).append(" instead\n"); stmt.close(); pstmt = con.prepareStatement("INSERT INTO HTMTEST (HTMID,RA,DECL,FLUX,COMMENT) VALUES (?,?,?,?,?)"); sb.append("1. inserting a record..."); pstmt.setLong(1, 1L); pstmt.setFloat(2, (float)1.2); pstmt.setDouble(3, 2.4); pstmt.setDouble(4, 3.2); pstmt.setString(5, "vlavbla"); pstmt.executeUpdate(); sb.append("success\n"); // try an update like bug #1757923 pstmt = con.prepareStatement("UPDATE HTMTEST set COMMENT=?, TYPE=? WHERE HTMID=?"); sb.append("2. updating record..."); pstmt.setString(1, "some update"); pstmt.setObject(2, (float)3.2); pstmt.setLong(3, 1L); pstmt.executeUpdate(); sb.append("success\n"); pstmt.close(); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); closeStmtResSet(pstmt, null); compareExpectedOutput("Test_PStypes", "0. false false\n" + "1. inserting a record...success\n" + "2. updating record...success\n" + "0. true true\n"); } private void Test_CallableStmt() { sb.setLength(0); // clear the output log buffer Statement stmt = null; CallableStatement cstmt = null; try { String tbl_nm = "tbl6402"; String proc_nm = "proc6402"; stmt = con.createStatement(); // create a test table. stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + tbl_nm + " (tint int, tdouble double, tbool boolean, tvarchar varchar(15), tclob clob, turl url, tclen int);"); sb.append("Created table: ").append(tbl_nm).append("\n"); // create a procedure with multiple different IN parameters which inserts a row into a table of which one column is computed. stmt.executeUpdate("CREATE PROCEDURE " + proc_nm + " (myint int, mydouble double, mybool boolean, myvarchar varchar(15), myclob clob, myurl url) BEGIN" + " INSERT INTO " + tbl_nm + " (tint, tdouble, tbool, tvarchar, tclob, turl, tclen) VALUES (myint, mydouble, mybool, myvarchar, myclob, myurl, LENGTH(myvarchar) + LENGTH(myclob)); " + "END;"); sb.append("Created procedure: ").append(proc_nm).append("\n"); // make sure we can call the procedure the old way (as string) stmt.executeUpdate("call " + proc_nm + "(1, 1.1, true,'one','ONE', 'www.monetdb.org');"); sb.append("Called procedure (1): ").append(proc_nm).append("\n"); showTblContents(tbl_nm); // now use a CallableStament object cstmt = con.prepareCall(" { call " + proc_nm + " (?,?, ?, ? , ?,?) } ;"); sb.append("Prepared Callable procedure: ").append(proc_nm).append("\n"); // specify first set of params cstmt.setInt(1, 2); cstmt.setDouble(2, 2.02); cstmt.setBoolean(3, true); cstmt.setString(4, "Two"); Clob myclob = con.createClob(); myclob.setString(1, "TWOs"); cstmt.setClob(5, myclob); cstmt.setString(6, "http://www.monetdb.org/"); cstmt.execute(); sb.append("Called Prepared procedure (1): ").append(proc_nm).append("\n"); showParams(cstmt); showTblContents(tbl_nm); myclob.setString(1, "TREEs"); // specify second set of params (some (1 and 3 and 5) are left the same) cstmt.setDouble(2, 3.02); cstmt.setString(4, "Tree"); try { cstmt.setURL(6, new java.net.URI("https://www.monetdb.org/").toURL()); } catch (java.net.URISyntaxException | java.net.MalformedURLException mfue) { sb.append("Invalid URL: ").append(mfue.getMessage()).append("\n"); } cstmt.execute(); sb.append("Called Prepared procedure (2): ").append(proc_nm).append("\n"); // showParams(cstmt); showTblContents(tbl_nm); // specify third set of params (some (1 and 2) are left the same) cstmt.setInt(1, 4); cstmt.setBoolean(3, false); cstmt.setString(4, "Four"); cstmt.executeUpdate(); sb.append("Called Prepared procedure (3): ").append(proc_nm).append("\n"); showTblContents(tbl_nm); // test setNull() also cstmt.setNull(3, Types.BOOLEAN); cstmt.setNull(5, Types.CLOB); cstmt.setNull(2, Types.DOUBLE); cstmt.setNull(4, Types.VARCHAR); cstmt.setNull(1, Types.INTEGER); cstmt.executeUpdate(); sb.append("Called Prepared procedure (with NULLs): ").append(proc_nm).append("\n"); showTblContents(tbl_nm); cstmt.clearParameters(); sb.append("Test completed. Cleanup procedure and table.\n"); stmt.execute("DROP PROCEDURE IF EXISTS " + proc_nm + ";"); stmt.execute("DROP TABLE IF EXISTS " + tbl_nm + ";"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); closeStmtResSet(cstmt, null); compareExpectedOutput("Test_CallableStmt", "Created table: tbl6402\n" + "Created procedure: proc6402\n" + "Called procedure (1): proc6402\n" + "Table tbl6402 has 7 columns:\n" + " tint tdouble tbool tvarchar tclob turl tclen\n" + " 1 1.1 true one ONE www.monetdb.org 6\n" + "Prepared Callable procedure: proc6402\n" + "Called Prepared procedure (1): proc6402\n" + "pmd. 6 parameters:\n" + "Param 1\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + " precision 10\n" + " scale 0\n" + " type 4\n" + " typename int\n" + " classname java.lang.Integer\n" + " mode 1 (IN)\n" + "Param 2\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + " precision 15\n" + " scale 0\n" + " type 8\n" + " typename double\n" + " classname java.lang.Double\n" + " mode 1 (IN)\n" + "Param 3\n" + " nullable 2 (UNKNOWN)\n" + " signed false\n" + " precision 1\n" + " scale 0\n" + " type 16\n" + " typename boolean\n" + " classname java.lang.Boolean\n" + " mode 1 (IN)\n" + "Param 4\n" + " nullable 2 (UNKNOWN)\n" + " signed false\n" + " precision 15\n" + " scale 0\n" + " type 12\n" + " typename varchar\n" + " classname java.lang.String\n" + " mode 1 (IN)\n" + "Param 5\n" + " nullable 2 (UNKNOWN)\n" + " signed false\n" + " precision 0\n" + " scale 0\n" + " type 12\n" + " typename " + (isPostDec2023 ? "varchar" : "clob") + "\n" + " classname java.lang.String\n" + " mode 1 (IN)\n" + "Param 6\n" + " nullable 2 (UNKNOWN)\n" + " signed false\n" + " precision 0\n" + " scale 0\n" + " type 12\n" + " typename url\n" + " classname org.monetdb.jdbc.types.URL\n" + " mode 1 (IN)\n" + "Table tbl6402 has 7 columns:\n" + " tint tdouble tbool tvarchar tclob turl tclen\n" + " 1 1.1 true one ONE www.monetdb.org 6\n" + " 2 2.02 true Two TWOs http://www.monetdb.org/ 7\n" + "Called Prepared procedure (2): proc6402\n" + "Table tbl6402 has 7 columns:\n" + " tint tdouble tbool tvarchar tclob turl tclen\n" + " 1 1.1 true one ONE www.monetdb.org 6\n" + " 2 2.02 true Two TWOs http://www.monetdb.org/ 7\n" + " 2 3.02 true Tree TWOs https://www.monetdb.org/ 8\n" + "Called Prepared procedure (3): proc6402\n" + "Table tbl6402 has 7 columns:\n" + " tint tdouble tbool tvarchar tclob turl tclen\n" + " 1 1.1 true one ONE www.monetdb.org 6\n" + " 2 2.02 true Two TWOs http://www.monetdb.org/ 7\n" + " 2 3.02 true Tree TWOs https://www.monetdb.org/ 8\n" + " 4 3.02 false Four TWOs https://www.monetdb.org/ 8\n" + "Called Prepared procedure (with NULLs): proc6402\n" + "Table tbl6402 has 7 columns:\n" + " tint tdouble tbool tvarchar tclob turl tclen\n" + " 1 1.1 true one ONE www.monetdb.org 6\n" + " 2 2.02 true Two TWOs http://www.monetdb.org/ 7\n" + " 2 3.02 true Tree TWOs https://www.monetdb.org/ 8\n" + " 4 3.02 false Four TWOs https://www.monetdb.org/ 8\n" + " null null null null null https://www.monetdb.org/ null\n" + "Test completed. Cleanup procedure and table.\n"); } private void Test_Rbooleans() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); int updates = stmt.executeUpdate( "CREATE TABLE Test_Rbooleans (" + " id int, tiny_int tinyint, small_int smallint, medium_int mediumint, \"integer\" int, big_int bigint," + " a_real real, a_float float, a_double double, a_decimal decimal(8,2), a_numeric numeric(8)," + " bool boolean, a_char char(4), b_char char(5), a_varchar varchar(20), PRIMARY KEY (id) )"); if (updates != Statement.SUCCESS_NO_INFO) sb.append("1a. Expected -2 got ").append(updates).append(" instead\n"); // all falses updates = stmt.executeUpdate("INSERT INTO Test_Rbooleans VALUES (1,0,0,0,0,0,0.0,0.0,0.0,0.0,0,false,'fals','false','false')"); // all trues updates += stmt.executeUpdate("INSERT INTO Test_Rbooleans VALUES (2,1,1,1,1,1,1.0,1.0,1.0,1.0,1,true,'true','true ','true')"); // sneakier updates += stmt.executeUpdate("INSERT INTO Test_Rbooleans VALUES (3,2,3,4,5,6,7.1,8.2,9.3,10.4,11,true,'TrUe','fAlSe','true/false')"); updates += stmt.executeUpdate("INSERT INTO Test_Rbooleans VALUES (4,2,3,4,5,6,7.1,8.2,9.3,10.4,11,true,'t ','f ','TRUE ')"); if (updates != 4) sb.append("1b. Expected 4 got ").append(updates).append(" instead\n"); rs = stmt.executeQuery("SELECT * FROM Test_Rbooleans ORDER BY id ASC"); // all should give false rs.next(); sb.append("1. ").append(rs.getInt("id")).append(", ").append(rs.getBoolean("tiny_int")) .append(", ").append(rs.getBoolean("small_int")).append(", ").append(rs.getBoolean("medium_int")) .append(", ").append(rs.getBoolean("integer")).append(", ").append(rs.getBoolean("big_int")) .append(", ").append(rs.getBoolean("a_real")).append(", ").append(rs.getBoolean("a_double")) .append(", ").append(rs.getBoolean("a_decimal")).append(", ").append(rs.getBoolean("a_numeric")) .append(", ").append(rs.getBoolean("bool")).append(", ").append(rs.getBoolean("a_char")) .append(", ").append(rs.getBoolean("b_char")).append(", ").append(rs.getBoolean("a_varchar")).append("\n"); // all should give true except the one before last rs.next(); sb.append("2. ").append(rs.getInt("id")).append(", ").append(rs.getBoolean("tiny_int")) .append(", ").append(rs.getBoolean("small_int")).append(", ").append(rs.getBoolean("medium_int")) .append(", ").append(rs.getBoolean("integer")).append(", ").append(rs.getBoolean("big_int")) .append(", ").append(rs.getBoolean("a_real")).append(", ").append(rs.getBoolean("a_double")) .append(", ").append(rs.getBoolean("a_decimal")).append(", ").append(rs.getBoolean("a_numeric")) .append(", ").append(rs.getBoolean("bool")).append(", ").append(rs.getBoolean("a_char")) .append(", ").append(rs.getBoolean("b_char")).append(", ").append(rs.getBoolean("a_varchar")).append("\n"); // should give true for all but the last two rs.next(); sb.append("3. ").append(rs.getInt("id")).append(", ").append(rs.getBoolean("tiny_int")) .append(", ").append(rs.getBoolean("small_int")).append(", ").append(rs.getBoolean("medium_int")) .append(", ").append(rs.getBoolean("integer")).append(", ").append(rs.getBoolean("big_int")) .append(", ").append(rs.getBoolean("a_real")).append(", ").append(rs.getBoolean("a_double")) .append(", ").append(rs.getBoolean("a_decimal")).append(", ").append(rs.getBoolean("a_numeric")) .append(", ").append(rs.getBoolean("bool")).append(", ").append(rs.getBoolean("a_char")) .append(", ").append(rs.getBoolean("b_char")).append(", ").append(rs.getBoolean("a_varchar")).append("\n"); // should give true for all but the last three rs.next(); sb.append("4. ").append(rs.getInt("id")).append(", ").append(rs.getBoolean("tiny_int")) .append(", ").append(rs.getBoolean("small_int")).append(", ").append(rs.getBoolean("medium_int")) .append(", ").append(rs.getBoolean("integer")).append(", ").append(rs.getBoolean("big_int")) .append(", ").append(rs.getBoolean("a_real")).append(", ").append(rs.getBoolean("a_double")) .append(", ").append(rs.getBoolean("a_decimal")).append(", ").append(rs.getBoolean("a_numeric")) .append(", ").append(rs.getBoolean("bool")).append(", ").append(rs.getBoolean("a_char")) .append(", ").append(rs.getBoolean("b_char")).append(", ").append(rs.getBoolean("a_varchar")).append("\n"); rs.next(); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Rbooleans", "0. false false\n" + "1. 1, false, false, false, false, false, false, false, false, false, false, false, false, false\n" + "2. 2, true, true, true, true, true, true, true, true, true, true, true, false, true\n" + "3. 3, true, true, true, true, true, true, true, true, true, true, true, false, false\n" + "4. 4, true, true, true, true, true, true, true, true, true, true, false, false, false\n" + "0. true true\n"); } private void Test_Rmetadata() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE Test_Rmetadata ( myint int, mydouble double, mybool boolean, myvarchar varchar(15), myclob clob )"); // all NULLs stmt.executeUpdate("INSERT INTO Test_Rmetadata VALUES (NULL, NULL, NULL, NULL, NULL)"); // all filled in stmt.executeUpdate("INSERT INTO Test_Rmetadata VALUES (2 , 3.0, true, 'A string', 'bla bla bla')"); rs = stmt.executeQuery("SELECT * FROM Test_Rmetadata"); ResultSetMetaData rsmd = rs.getMetaData(); sb.append("0. ").append(rsmd.getColumnCount()).append(" columns:\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append("Colnr ").append(col).append(".\n"); sb.append("\tclassname ").append(rsmd.getColumnClassName(col)).append("\n"); sb.append("\tdisplaysize ").append(rsmd.getColumnDisplaySize(col)).append("\n"); sb.append("\tlabel ").append(rsmd.getColumnLabel(col)).append("\n"); sb.append("\tname ").append(rsmd.getColumnName(col)).append("\n"); sb.append("\ttype ").append(rsmd.getColumnType(col)).append("\n"); sb.append("\ttypename ").append(rsmd.getColumnTypeName(col)).append("\n"); sb.append("\tprecision ").append(rsmd.getPrecision(col)).append("\n"); sb.append("\tscale ").append(rsmd.getScale(col)).append("\n"); sb.append("\tcatalogname ").append(rsmd.getCatalogName(col)).append("\n"); sb.append("\tschemaname ").append(rsmd.getSchemaName(col)).append("\n"); sb.append("\ttablename ").append(rsmd.getTableName(col)).append("\n"); sb.append("\tautoincrement ").append(rsmd.isAutoIncrement(col)).append("\n"); sb.append("\tcasesensitive ").append(rsmd.isCaseSensitive(col)).append("\n"); sb.append("\tcurrency ").append(rsmd.isCurrency(col)).append("\n"); sb.append("\tdefwritable ").append(rsmd.isDefinitelyWritable(col)).append("\n"); sb.append("\tnullable ").append(rsmd.isNullable(col)).append("\n"); sb.append("\treadonly ").append(rsmd.isReadOnly(col)).append("\n"); sb.append("\tsearchable ").append(rsmd.isSearchable(col)).append("\n"); sb.append("\tsigned ").append(rsmd.isSigned(col)).append("\n"); sb.append("\twritable ").append(rsmd.isWritable(col)).append("\n"); } for (int i = 6; rs.next(); i++) { for (int col = 1; col <= rsmd.getColumnCount(); col++) { Object obj = rs.getObject(col); String type = rsmd.getColumnClassName(col); String isInstance = "(null)"; if (obj != null && type != null) { try { Class<?> c = Class.forName(type); if (c.isInstance(obj)) { isInstance = (obj.getClass().getName() + " is an instance of " + type); } else { isInstance = (obj.getClass().getName() + " is NOT an instance of " + type); } } catch (ClassNotFoundException e) { isInstance = "No such class: " + type; } } sb.append(i).append(".\t").append(isInstance).append("\n"); } } rs.close(); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Rmetadata", "0. false false\n" + "0. 5 columns:\n" + "Colnr 1.\n" + " classname java.lang.Integer\n" + " displaysize 1\n" + " label myint\n" + " name myint\n" + " type 4\n" + " typename int\n" + " precision 10\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "Colnr 2.\n" + " classname java.lang.Double\n" + " displaysize 24\n" + " label mydouble\n" + " name mydouble\n" + " type 8\n" + " typename double\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "Colnr 3.\n" + " classname java.lang.Boolean\n" + " displaysize 5\n" + " label mybool\n" + " name mybool\n" + " type 16\n" + " typename boolean\n" + " precision 1\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "Colnr 4.\n" + " classname java.lang.String\n" + " displaysize 8\n" + " label myvarchar\n" + " name myvarchar\n" + " type 12\n" + " typename varchar\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "Colnr 5.\n" + " classname java.lang.String\n" + " displaysize 11\n" + " label myclob\n" + " name myclob\n" + " type 12\n" + " typename " + (isPostDec2023 ? "varchar" : "clob") + "\n" + " precision 11\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "7. java.lang.Integer is an instance of java.lang.Integer\n" + "7. java.lang.Double is an instance of java.lang.Double\n" + "7. java.lang.Boolean is an instance of java.lang.Boolean\n" + "7. java.lang.String is an instance of java.lang.String\n" + "7. java.lang.String is an instance of java.lang.String\n" + "0. true true\n"); } private void Test_RSgetMetaData() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE Test_RSmetadata ( myint int, mydouble double, mybool boolean, myvarchar varchar(15), myclob clob )"); // all NULLs stmt.executeUpdate("INSERT INTO Test_RSmetadata VALUES (NULL, NULL, NULL, NULL, NULL)"); // all filled in stmt.executeUpdate("INSERT INTO Test_RSmetadata VALUES (2 , 3.0, true, 'A string', 'bla bla bla')"); rs = stmt.executeQuery("SELECT * FROM Test_RSmetadata"); sb.append("0. ").append(rs.getMetaData().getColumnCount()).append(" columns:\n"); for (int col = 1; col <= rs.getMetaData().getColumnCount(); col++) { sb.append("Colnr ").append(col).append(".\n"); sb.append("\tclassname ").append(rs.getMetaData().getColumnClassName(col)).append("\n"); sb.append("\tdisplaysize ").append(rs.getMetaData().getColumnDisplaySize(col)).append("\n"); sb.append("\tlabel ").append(rs.getMetaData().getColumnLabel(col)).append("\n"); sb.append("\tname ").append(rs.getMetaData().getColumnName(col)).append("\n"); sb.append("\ttype ").append(rs.getMetaData().getColumnType(col)).append("\n"); sb.append("\ttypename ").append(rs.getMetaData().getColumnTypeName(col)).append("\n"); sb.append("\tprecision ").append(rs.getMetaData().getPrecision(col)).append("\n"); sb.append("\tscale ").append(rs.getMetaData().getScale(col)).append("\n"); sb.append("\tcatalogname ").append(rs.getMetaData().getCatalogName(col)).append("\n"); sb.append("\tschemaname ").append(rs.getMetaData().getSchemaName(col)).append("\n"); sb.append("\ttablename ").append(rs.getMetaData().getTableName(col)).append("\n"); sb.append("\tautoincrement ").append(rs.getMetaData().isAutoIncrement(col)).append("\n"); sb.append("\tcasesensitive ").append(rs.getMetaData().isCaseSensitive(col)).append("\n"); sb.append("\tcurrency ").append(rs.getMetaData().isCurrency(col)).append("\n"); sb.append("\tdefwritable ").append(rs.getMetaData().isDefinitelyWritable(col)).append("\n"); sb.append("\tnullable ").append(rs.getMetaData().isNullable(col)).append("\n"); sb.append("\treadonly ").append(rs.getMetaData().isReadOnly(col)).append("\n"); sb.append("\tsearchable ").append(rs.getMetaData().isSearchable(col)).append("\n"); sb.append("\tsigned ").append(rs.getMetaData().isSigned(col)).append("\n"); sb.append("\twritable ").append(rs.getMetaData().isWritable(col)).append("\n"); } for (int i = 6; rs.next(); i++) { for (int col = 1; col <= rs.getMetaData().getColumnCount(); col++) { Object obj = rs.getObject(col); String type = rs.getMetaData().getColumnClassName(col); String isInstance = "(null)"; if (obj != null && type != null) { try { Class<?> c = Class.forName(type); if (c.isInstance(obj)) { isInstance = (obj.getClass().getName() + " is an instance of " + type); } else { isInstance = (obj.getClass().getName() + " is NOT an instance of " + type); } } catch (ClassNotFoundException e) { isInstance = "No such class: " + type; } } sb.append(i).append(".\t").append(isInstance).append("\n"); } } rs.close(); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_RSgetMetaData", "0. false false\n" + "0. 5 columns:\n" + "Colnr 1.\n" + " classname java.lang.Integer\n" + " displaysize 1\n" + " label myint\n" + " name myint\n" + " type 4\n" + " typename int\n" + " precision 10\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "Colnr 2.\n" + " classname java.lang.Double\n" + " displaysize 24\n" + " label mydouble\n" + " name mydouble\n" + " type 8\n" + " typename double\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed true\n" + " writable false\n" + "Colnr 3.\n" + " classname java.lang.Boolean\n" + " displaysize 5\n" + " label mybool\n" + " name mybool\n" + " type 16\n" + " typename boolean\n" + " precision 1\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsmetadata\n" + " autoincrement false\n" + " casesensitive false\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "Colnr 4.\n" + " classname java.lang.String\n" + " displaysize 8\n" + " label myvarchar\n" + " name myvarchar\n" + " type 12\n" + " typename varchar\n" + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "Colnr 5.\n" + " classname java.lang.String\n" + " displaysize 11\n" + " label myclob\n" + " name myclob\n" + " type 12\n" + " typename " + (isPostDec2023 ? "varchar" : "clob") + "\n" + " precision 11\n" + " scale 0\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsmetadata\n" + " autoincrement false\n" + " casesensitive true\n" + " currency false\n" + " defwritable false\n" + " nullable 1\n" + " readonly true\n" + " searchable true\n" + " signed false\n" + " writable false\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "6. (null)\n" + "7. java.lang.Integer is an instance of java.lang.Integer\n" + "7. java.lang.Double is an instance of java.lang.Double\n" + "7. java.lang.Boolean is an instance of java.lang.Boolean\n" + "7. java.lang.String is an instance of java.lang.String\n" + "7. java.lang.String is an instance of java.lang.String\n" + "0. true true\n"); } private void Test_RfetchManyColumnsInfo() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { final int NR_COLUMNS = 180; final StringBuilder sql = new StringBuilder(50 + (NR_COLUMNS * 12)); sql.append("CREATE TABLE Test_RfetchManyColumnsInfo ("); for (int col = 1; col <= NR_COLUMNS; col++) { sql.append("col").append(col).append(" int"); sql.append((col < NR_COLUMNS) ? ", " : ")"); } stmt = con.createStatement(); stmt.executeUpdate(sql.toString()); // add 1 row (all NULLs) int inserted = stmt.executeUpdate("INSERT INTO Test_RfetchManyColumnsInfo (col1) VALUES (1)"); if (inserted != 1) sb.append("Expected 1 row inserted, but got: ").append(inserted).append("\n"); rs = stmt.executeQuery("SELECT * FROM Test_RfetchManyColumnsInfo"); rs.next(); ResultSetMetaData rsmd = rs.getMetaData(); sb.append(rsmd.getColumnCount()).append(" columns start at columnCount\n"); // do pulling of the metadata info in reverse order to test optimizing logic // in ResultSetMetaData.fetchManyColumnsInfo() to choose a lower start_col iteratively for (int col = rsmd.getColumnCount(); col >= 1; col--) { // sb.append(col).append(","); rsmd.getColumnClassName(col); rsmd.getColumnDisplaySize(col); rsmd.getColumnLabel(col); rsmd.getColumnName(col); rsmd.getColumnType(col); rsmd.getColumnTypeName(col); rsmd.getPrecision(col); rsmd.getScale(col); rsmd.getCatalogName(col); rsmd.getSchemaName(col); rsmd.getTableName(col); rsmd.isAutoIncrement(col); rsmd.isCaseSensitive(col); rsmd.isCurrency(col); rsmd.isDefinitelyWritable(col); if (rsmd.isNullable(col) != ResultSetMetaData.columnNullable) sb.append(col).append(" wrong isNullable()").append(rsmd.isNullable(col)).append("\n"); rsmd.isReadOnly(col); rsmd.isSearchable(col); rsmd.isSigned(col); rsmd.isWritable(col); } rs.close(); rs = stmt.executeQuery("SELECT * FROM Test_RfetchManyColumnsInfo"); rs.next(); rsmd = rs.getMetaData(); sb.append(rsmd.getColumnCount()).append(" columns start at 1\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { // sb.append(col).append(","); rsmd.getColumnClassName(col); rsmd.getColumnDisplaySize(col); rsmd.getColumnLabel(col); rsmd.getColumnName(col); rsmd.getColumnType(col); rsmd.getColumnTypeName(col); rsmd.getPrecision(col); rsmd.getScale(col); rsmd.getCatalogName(col); rsmd.getSchemaName(col); rsmd.getTableName(col); rsmd.isAutoIncrement(col); rsmd.isCaseSensitive(col); rsmd.isCurrency(col); rsmd.isDefinitelyWritable(col); if (rsmd.isNullable(col) != ResultSetMetaData.columnNullable) sb.append(col).append(" wrong isNullable()").append(rsmd.isNullable(col)).append("\n"); rsmd.isReadOnly(col); rsmd.isSearchable(col); rsmd.isSigned(col); rsmd.isWritable(col); } rs.close(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup table try { stmt.executeUpdate("DROP TABLE IF EXISTS Test_RfetchManyColumnsInfo;"); } catch (SQLException e) { sb.append("FAILED to drop: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_RfetchManyColumnsInfo", "180 columns start at columnCount\n" + "180 columns start at 1\n"); } private void Test_Rpositioning() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); // get a one rowed resultset rs = stmt.executeQuery("SELECT 1"); // >> true: we should be before the first result now sb.append("1. true\t").append(rs.isBeforeFirst()).append("\n"); // >> false: we're not at the first result sb.append("2. false\t").append(rs.isFirst()).append("\n"); // >> true: there is one result, so we can call next once sb.append("3. true\t").append(rs.next()).append("\n"); // >> false: we're not before the first row anymore sb.append("4. false\t").append(rs.isBeforeFirst()).append("\n"); // >> true: we're at the first result sb.append("5. true\t").append(rs.isFirst()).append("\n"); // >> false: we're on the last row sb.append("6. false\t").append(rs.isAfterLast()).append("\n"); // >> true: see above sb.append("7. true\t").append(rs.isLast()).append("\n"); // >> false: there is one result, so this is it sb.append("8. false\t").append(rs.next()).append("\n"); // >> true: yes, we're at the end sb.append("9. true\t").append(rs.isAfterLast()).append("\n"); // >> false: no we're one over it sb.append("10. false\t").append(rs.isLast()).append("\n"); // >> false: another try to move on should still fail sb.append("11. false\t").append(rs.next()).append("\n"); // >> true: and we should stay positioned after the last sb.append("12.true\t").append(rs.isAfterLast()).append("\n"); rs.close(); // try the same with a scrollable result set DatabaseMetaData dbmd = con.getMetaData(); rs = dbmd.getTableTypes(); // >> true: we should be before the first result now sb.append("1. true\t").append(rs.isBeforeFirst()).append("\n"); // >> false: we're not at the first result sb.append("2. false\t").append(rs.isFirst()).append("\n"); // >> true: there is one result, so we can call next once sb.append("3. true\t").append(rs.next()).append("\n"); // >> false: we're not before the first row anymore sb.append("4. false\t").append(rs.isBeforeFirst()).append("\n"); // >> true: we're at the first result sb.append("5. true\t").append(rs.isFirst()).append("\n"); // move to last row rs.last(); // >> false: we're on the last row sb.append("6. false\t").append(rs.isAfterLast()).append("\n"); // >> true: see above sb.append("7. true\t").append(rs.isLast()).append("\n"); // >> false: there is one result, so this is it sb.append("8. false\t").append(rs.next()).append("\n"); // >> true: yes, we're at the end sb.append("9. true\t").append(rs.isAfterLast()).append("\n"); // >> false: no we're one over it sb.append("10. false\t").append(rs.isLast()).append("\n"); // >> false: another try to move on should still fail sb.append("11. false\t").append(rs.next()).append("\n"); // >> true: and we should stay positioned after the last sb.append("12. true\t").append(rs.isAfterLast()).append("\n"); rs.close(); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Rpositioning", "1. true true\n" + "2. false false\n" + "3. true true\n" + "4. false false\n" + "5. true true\n" + "6. false false\n" + "7. true true\n" + "8. false false\n" + "9. true true\n" + "10. false false\n" + "11. false false\n" + "12.true true\n" + "1. true true\n" + "2. false false\n" + "3. true true\n" + "4. false false\n" + "5. true true\n" + "6. false false\n" + "7. true true\n" + "8. false false\n" + "9. true true\n" + "10. false false\n" + "11. false false\n" + "12. true true\n"); } private void Test_Rsqldata() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; ResultSetMetaData rsmd = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE Test_Rsqldata ( myinet inet, myurl url )"); String InsertInto = "INSERT INTO Test_Rsqldata VALUES "; // all NULLs stmt.executeUpdate(InsertInto + "(NULL, NULL)"); // all filled in stmt.executeUpdate(InsertInto + "('172.5.5.5' , 'http://www.monetdb.org/')"); stmt.executeUpdate(InsertInto + "('172.5.5.5/32' , 'http://www.monetdb.org/Home')"); stmt.executeUpdate(InsertInto + "('172.5.5.5/16' , 'http://www.monetdb.org/Home#someanchor')"); stmt.executeUpdate(InsertInto + "('172.5.5.5/26' , 'http://www.monetdb.org/?query=bla')"); rs = stmt.executeQuery("SELECT * FROM Test_Rsqldata"); rsmd = rs.getMetaData(); sb.append("0. ").append(rsmd.getColumnCount()).append(" columns:\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append(col).append("\n"); sb.append("\tclassname ").append(rsmd.getColumnClassName(col)).append("\n"); sb.append("\tcatalogname ").append(rsmd.getCatalogName(col)).append("\n"); sb.append("\tschemaname ").append(rsmd.getSchemaName(col)).append("\n"); sb.append("\ttablename ").append(rsmd.getTableName(col)).append("\n"); sb.append("\tcolumnname ").append(rsmd.getColumnName(col)).append("\n"); } for (int i = 1; rs.next(); i++) { for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append(i).append(".\t"); Object x = rs.getObject(col); if (x == null) { sb.append("<null>\n"); } else { sb.append(x.toString()).append("\n"); if (x instanceof INET) { INET inet = (INET)x; sb.append("\t").append(inet.getAddress()).append("/").append(inet.getNetmaskBits()).append("\n"); sb.append("\t").append(inet.getInetAddress().toString()).append("\n"); } else if (x instanceof URL) { URL url = (URL)x; sb.append("\t").append(url.getURL().toString()).append("\n"); } } } } rs.close(); con.rollback(); con.setAutoCommit(true); // >> true: auto commit was just switched on sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Rsqldata", "0. false false\n" + "0. 2 columns:\n" + "1\n" + " classname org.monetdb.jdbc.types.INET\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsqldata\n" + " columnname myinet\n" + "2\n" + " classname org.monetdb.jdbc.types.URL\n" + " catalogname null\n" + " schemaname sys\n" + " tablename test_rsqldata\n" + " columnname myurl\n" + "1. <null>\n" + "1. <null>\n" + "2. 172.5.5.5\n" + " 172.5.5.5/32\n" + " /172.5.5.5\n" + "2. http://www.monetdb.org/\n" + " http://www.monetdb.org/\n" + "3. 172.5.5.5\n" + " 172.5.5.5/32\n" + " /172.5.5.5\n" + "3. http://www.monetdb.org/Home\n" + " http://www.monetdb.org/Home\n" + "4. 172.5.5.5/16\n" + " 172.5.5.5/16\n" + " /172.5.5.5\n" + "4. http://www.monetdb.org/Home#someanchor\n" + " http://www.monetdb.org/Home#someanchor\n" + "5. 172.5.5.5/26\n" + " 172.5.5.5/26\n" + " /172.5.5.5\n" + "5. http://www.monetdb.org/?query=bla\n" + " http://www.monetdb.org/?query=bla\n" + "0. true true\n"); } private void Test_Rtimedate() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE table_Test_Rtimedate ( id int PRIMARY KEY, ts timestamp, t time, d date, vc varchar(30) )"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (1, timestamp '2004-04-24 11:43:53.123')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, t) VALUES (2, time '11:43:53.123')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (3, date '2004-04-24')"); // same values but now as strings to test string to timestamp / time / date object conversions stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (4, '2004-04-24 11:43:53.654321')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (5, '11:43:53')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (6, '2004-04-24')"); // test also with small years (< 1000) (see bug 6468) stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (11, timestamp '904-04-24 11:43:53.567')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (12, timestamp '74-04-24 11:43:53.567')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (13, timestamp '4-04-24 11:43:53.567')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (14, date '904-04-24')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (15, date '74-04-24')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (16, date '4-04-24')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (17, '904-04-24 11:43:53.567')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (18, '74-04-24 11:43:53.567')"); stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (19, '4-04-24 11:43:53.567')"); // test also with negative years (see bug 6468) stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (21, timestamp '-4-04-24 11:43:53.567')"); // negative year stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, ts) VALUES (22, timestamp '-2004-04-24 11:43:53.567')"); // negative year stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (23, date '-4-04-24')"); // negative year stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, d) VALUES (24, date '-3004-04-24')"); // negative year stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (25, '-2004-04-24 11:43:53.654321')"); // negative year stmt.executeUpdate("INSERT INTO table_Test_Rtimedate(id, vc) VALUES (26, '-3004-04-24')"); // negative year rs = stmt.executeQuery("SELECT * FROM table_Test_Rtimedate"); readNextRow(rs, 1, "ts"); readNextRow(rs, 2, "t"); readNextRow(rs, 3, "d"); readNextRow(rs, 4, "vc"); readNextRow(rs, 5, "vc"); readNextRow(rs, 6, "vc"); readNextRow(rs, 11, "ts"); readNextRow(rs, 12, "ts"); readNextRow(rs, 13, "ts"); readNextRow(rs, 14, "d"); readNextRow(rs, 15, "d"); readNextRow(rs, 16, "d"); readNextRow(rs, 17, "vc"); readNextRow(rs, 18, "vc"); readNextRow(rs, 19, "vc"); readNextRow(rs, 21, "ts"); readNextRow(rs, 22, "ts"); readNextRow(rs, 23, "d"); readNextRow(rs, 24, "d"); readNextRow(rs, 25, "vc"); readNextRow(rs, 26, "vc"); readWarnings(stmt.getWarnings()); readWarnings(con.getWarnings()); con.rollback(); con.setAutoCommit(true); // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("Test_Rtimedate", "0. false false\n" + "1. ts 2004-04-24 11:43:53.123000 to ts: 2004-04-24 11:43:53.123\n" + "1. ts 2004-04-24 11:43:53.123000 to tm: 11:43:53\n" + "1. ts 2004-04-24 11:43:53.123000 to dt: 2004-04-24\n" + "1. ts 2004-04-24 11:43:53.123000 to LocalDateTime: 2004-04-24T11:43:53.123\n" + "2. t 11:43:53 to ts: 1970-01-01 11:43:53.0\n" + "2. t 11:43:53 to tm: 11:43:53\n" + "2. t 11:43:53 to dt: 1970-01-01\n" + "2. t 11:43:53 to LocalTime: 11:43:53\n" + "3. d 2004-04-24 to ts: 2004-04-24 00:00:00.0\n" + "3. d 2004-04-24 to tm: 00:00:00\n" + "3. d 2004-04-24 to dt: 2004-04-24\n" + "3. d 2004-04-24 to LocalDate: 2004-04-24\n" + "4. vc 2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" + "4. vc 2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 5 found: '-' in '2004-04-24 11:43:53.654321'\n" + "4. vc 2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" + "5. vc 11:43:53 to ts: rs.getTimestamp(colnm) failed with error: parsing failed at pos 3 found: ':' in '11:43:53'\n" + "5. vc 11:43:53 to tm: 11:43:53\n" + "5. vc 11:43:53 to dt: rs.getDate(colnm) failed with error: parsing failed at pos 3 found: ':' in '11:43:53'\n" + "6. vc 2004-04-24 to ts: 2004-04-24 00:00:00.0\n" + "6. vc 2004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 5 found: '-' in '2004-04-24'\n" + "6. vc 2004-04-24 to dt: 2004-04-24\n" + "11. ts 904-04-24 11:43:53.567000 to ts: 0904-04-24 11:43:53.567\n" + "11. ts 904-04-24 11:43:53.567000 to tm: 11:43:53\n" + "11. ts 904-04-24 11:43:53.567000 to dt: 0904-04-24\n" + "11. ts 904-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '904-04-24T11:43:53.567000' could not be parsed at index 0\n" + "12. ts 74-04-24 11:43:53.567000 to ts: 0074-04-24 11:43:53.567\n" + "12. ts 74-04-24 11:43:53.567000 to tm: 11:43:53\n" + "12. ts 74-04-24 11:43:53.567000 to dt: 0074-04-24\n" + "12. ts 74-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '74-04-24T11:43:53.567000' could not be parsed at index 0\n" + "13. ts 4-04-24 11:43:53.567000 to ts: 0004-04-24 11:43:53.567\n" + "13. ts 4-04-24 11:43:53.567000 to tm: 11:43:53\n" + "13. ts 4-04-24 11:43:53.567000 to dt: 0004-04-24\n" + "13. ts 4-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '4-04-24T11:43:53.567000' could not be parsed at index 0\n" + "14. d 904-04-24 to ts: 0904-04-24 00:00:00.0\n" + "14. d 904-04-24 to tm: 00:00:00\n" + "14. d 904-04-24 to dt: 0904-04-24\n" + "14. d 904-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '904-04-24' could not be parsed at index 0\n" + "15. d 74-04-24 to ts: 0074-04-24 00:00:00.0\n" + "15. d 74-04-24 to tm: 00:00:00\n" + "15. d 74-04-24 to dt: 0074-04-24\n" + "15. d 74-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '74-04-24' could not be parsed at index 0\n" + "16. d 4-04-24 to ts: 0004-04-24 00:00:00.0\n" + "16. d 4-04-24 to tm: 00:00:00\n" + "16. d 4-04-24 to dt: 0004-04-24\n" + "16. d 4-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '4-04-24' could not be parsed at index 0\n" + "17. vc 904-04-24 11:43:53.567 to ts: 0904-04-24 11:43:53.567\n" + "17. vc 904-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 4 found: '-' in '904-04-24 11:43:53.567'\n" + "17. vc 904-04-24 11:43:53.567 to dt: 0904-04-24\n" + "18. vc 74-04-24 11:43:53.567 to ts: 0074-04-24 11:43:53.567\n" + "18. vc 74-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 3 found: '-' in '74-04-24 11:43:53.567'\n" + "18. vc 74-04-24 11:43:53.567 to dt: 0074-04-24\n" + "19. vc 4-04-24 11:43:53.567 to ts: 0004-04-24 11:43:53.567\n" + "19. vc 4-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 2 found: '-' in '4-04-24 11:43:53.567'\n" + "19. vc 4-04-24 11:43:53.567 to dt: 0004-04-24\n" + "21. ts -4-04-24 11:43:53.567000 to ts: 0004-04-24 11:43:53.567\n" + "21. ts -4-04-24 11:43:53.567000 to tm: 11:43:53\n" + "21. ts -4-04-24 11:43:53.567000 to dt: 0004-04-24\n" + "21. ts -4-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '-4-04-24T11:43:53.567000' could not be parsed at index 1\n" + "22. ts -2004-04-24 11:43:53.567000 to ts: 2004-04-24 11:43:53.567\n" + "22. ts -2004-04-24 11:43:53.567000 to tm: 11:43:53\n" + "22. ts -2004-04-24 11:43:53.567000 to dt: 2004-04-24\n" + "22. ts -2004-04-24 11:43:53.567000 to LocalDateTime: -2004-04-24T11:43:53.567\n" + "23. d -4-04-24 to ts: 0004-04-24 00:00:00.0\n" + "23. d -4-04-24 to tm: 00:00:00\n" + "23. d -4-04-24 to dt: 0004-04-24\n" + "23. d -4-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '-4-04-24' could not be parsed at index 1\n" + "24. d -3004-04-24 to ts: 3004-04-24 00:00:00.0\n" + "24. d -3004-04-24 to tm: 00:00:00\n" + "24. d -3004-04-24 to dt: 3004-04-24\n" + "24. d -3004-04-24 to LocalDate: -3004-04-24\n" + "25. vc -2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" + "25. vc -2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 6 found: '-' in '-2004-04-24 11:43:53.654321'\n" + "25. vc -2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" + "26. vc -3004-04-24 to ts: 3004-04-24 00:00:00.0\n" + "26. vc -3004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 6 found: '-' in '-3004-04-24'\n" + "26. vc -3004-04-24 to dt: 3004-04-24\n" + "0. true true\n"); } private void readNextRow(ResultSet rs, int rowseq, String colnm) throws SQLException { rs.next(); readWarnings(rs.getWarnings()); rs.clearWarnings(); // fetch the column value using multiple methods: getString(), getTimestamp(), getTime() and getDate() // to test proper conversion and error reporting String data = rs.getString("id") + ". " + colnm + " " + rs.getString(colnm) + " to "; // getTimestamp() may raise a conversion warning when the value is of type Time or a String which doesn't match format yyyy-mm-dd hh:mm:ss try { sb.append(data).append("ts: ").append(rs.getTimestamp(colnm)).append("\n"); } catch (SQLException e) { sb.append("rs.getTimestamp(colnm) failed with error: ").append(e.getMessage()).append("\n"); } readWarnings(rs.getWarnings()); rs.clearWarnings(); // getTime() may raise a conversion warning when the value is of type Date or a String which doesn't match format hh:mm:ss try { sb.append(data).append("tm: ").append(rs.getTime(colnm)).append("\n"); } catch (SQLException e) { sb.append("rs.getTime(colnm) failed with error: ").append(e.getMessage()).append("\n"); } readWarnings(rs.getWarnings()); rs.clearWarnings(); // getDate() may raise a conversion warning when the value is of type Time or a String which doesn't match format yyyy-mm-dd try { sb.append(data).append("dt: ").append(rs.getDate(colnm)).append("\n"); } catch (SQLException e) { sb.append("rs.getDate(colnm) failed with error: ").append(e.getMessage()).append("\n"); } readWarnings(rs.getWarnings()); rs.clearWarnings(); // getObject(colnm, class<T>) may raise a conversion error try { switch(colnm) { case "d": sb.append(data).append("LocalDate: ").append(rs.getObject(colnm, java.time.LocalDate.class)).append("\n"); break; case "ts": sb.append(data).append("LocalDateTime: ").append(rs.getObject(colnm, java.time.LocalDateTime.class)).append("\n"); break; case "t": sb.append(data).append("LocalTime: ").append(rs.getObject(colnm, java.time.LocalTime.class)).append("\n"); break; } } catch (SQLException e) { sb.append("rs.getObject(colnm, class<T>): ").append(e.getMessage()).append("\n"); } readWarnings(rs.getWarnings()); rs.clearWarnings(); } private void Test_Sbatching() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con.setAutoCommit(false); // >> false: auto commit should be off now sb.append("0. false\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("1. create..."); if (stmt.executeUpdate("CREATE TABLE Test_Sbatching ( id int )") != Statement.SUCCESS_NO_INFO) sb.append("Wrong return status\n"); else sb.append("passed\n"); // for large batches of DML always set sys.optimizer = 'minimal_pipe'. It makes a big difference in performance. stmt.execute("SET sys.optimizer = 'minimal_pipe'"); // start batching a large amount of inserts for (int i = 1; i <= 3432; i++) { stmt.addBatch("INSERT INTO Test_Sbatching VALUES (" + i + ")"); if (i % 1500 == 0) { sb.append("2. executing batch (1500 inserts)..."); int[] cnts = stmt.executeBatch(); sb.append("passed\n"); sb.append("3. checking number of update counts..."); if (cnts.length != 1500) sb.append("Invalid size: ").append(cnts.length); sb.append(cnts.length).append(" passed\n"); sb.append("4. checking update counts (should all be 1)..."); for (int j = 0; j < cnts.length; j++) { if (cnts[j] != 1) sb.append("Unexpected value: ").append(cnts[j]); } sb.append("passed\n"); con.commit(); } } sb.append("5. executing final batch..."); stmt.executeBatch(); con.commit(); sb.append("passed\n"); pstmt = con.prepareStatement("INSERT INTO Test_Sbatching VALUES (?)"); // start batching a large amount of prepared inserts using JDBC 4.2 executeLargeBatch() for (int i = 1; i <= 3568; i++) { pstmt.setInt(1, i); pstmt.addBatch(); if (i % 3000 == 0) { sb.append("2. executing batch (3000 inserts)..."); long[] cnts = pstmt.executeLargeBatch(); sb.append("passed\n"); sb.append("3. checking number of update counts..."); if (cnts.length != 3000) sb.append("Invalid size: ").append(cnts.length); sb.append(cnts.length).append(" passed\n"); sb.append("4. checking update counts (should all be 1)..."); for (int j = 0; j < cnts.length; j++) { if (cnts[j] != 1) sb.append("Unexpected value: ").append(cnts[j]); } sb.append("passed\n"); con.commit(); } } sb.append("5. executing final Largebatch..."); pstmt.executeLargeBatch(); con.commit(); sb.append("passed\n"); sb.append("6. clearing the batch..."); stmt.clearBatch(); pstmt.clearBatch(); sb.append("passed\n"); sb.append("7. checking table count..."); rs = stmt.executeQuery("SELECT COUNT(*) FROM Test_Sbatching"); rs.next(); sb.append(rs.getInt(1)).append(" passed\n"); sb.append("8. drop table..."); if (stmt.executeUpdate("DROP TABLE Test_Sbatching") != Statement.SUCCESS_NO_INFO) sb.append("Wrong return status\n"); else sb.append("passed\n"); // rs.close(); stmt.close(); pstmt.close(); con.commit(); con.setAutoCommit(true); // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); closeStmtResSet(pstmt, null); compareExpectedOutput("Test_Sbatching", "0. false false\n" + "1. create...passed\n" + "2. executing batch (1500 inserts)...passed\n" + "3. checking number of update counts...1500 passed\n" + "4. checking update counts (should all be 1)...passed\n" + "2. executing batch (1500 inserts)...passed\n" + "3. checking number of update counts...1500 passed\n" + "4. checking update counts (should all be 1)...passed\n" + "5. executing final batch...passed\n" + "2. executing batch (3000 inserts)...passed\n" + "3. checking number of update counts...3000 passed\n" + "4. checking update counts (should all be 1)...passed\n" + "5. executing final Largebatch...passed\n" + "6. clearing the batch...passed\n" + "7. checking table count...7000 passed\n" + "8. drop table...passed\n" + "0. true true\n"); } private void Test_SgeneratedKeys() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rsk = null; try { stmt = con.createStatement(); // test getGeneratedKeys rsk = stmt.getGeneratedKeys(); // test meta data retrieval of this MonetVirtualResultSet. // It used to fail with: Exception in thread "main" java.lang.IllegalArgumentException: Header may not be null! final ResultSetMetaData rsmd = rsk.getMetaData(); sb.append("rsmd has ").append(rsmd.getColumnCount()).append(" columns\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append("ColumnName: ").append(rsmd.getColumnName(col)) .append(" ColumnTypeName: ").append(rsmd.getColumnTypeName(col)) .append(" Precision: ").append(rsmd.getPrecision(col)) .append(" Scale: ").append(rsmd.getScale(col)) .append(" ColumnDisplaySize: ").append(rsmd.getColumnDisplaySize(col)) .append(" ColumnType: ").append(rsmd.getColumnType(col)) .append(" ColumnClassName: ").append(rsmd.getColumnClassName(col)) .append(" isNullable: ").append(rsmd.isNullable(col)) .append(" isAutoIncrement: ").append(rsmd.isAutoIncrement(col)) .append("\n"); } rsk.close(); rsk = null; stmt.close(); stmt = null; } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rsk); compareExpectedOutput("Test_SgeneratedKeys", "rsmd has 1 columns\n" + "ColumnName: GENERATED_KEY ColumnTypeName: bigint Precision: 19 Scale: 0 ColumnDisplaySize: 20 ColumnType: -5 ColumnClassName: java.lang.Long isNullable: 2 isAutoIncrement: false\n"); } private void Test_Smoreresults() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("1. more results?..."); if (stmt.getMoreResults() != false || stmt.getUpdateCount() != -1) sb.append("more results on an unitialised Statement, how can that be?\n"); sb.append(" nope :)\n"); sb.append("2. SELECT 1..."); if (stmt.execute("SELECT 1;") == false) sb.append("SELECT 1 returns update or no results\n"); sb.append(" ResultSet :)\n"); sb.append("3. more results?..."); if (stmt.getMoreResults() != false || stmt.getUpdateCount() != -1) sb.append("more results after SELECT 1 query, how can that be?\n"); sb.append(" nope :)\n"); sb.append("4. even more results?..."); if (stmt.getMoreResults() != false) sb.append("still more results after SELECT 1 query, how can that be?\n"); sb.append(" nope :)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); compareExpectedOutput("Test_Smoreresults", "0. true true\n" + "1. more results?... nope :)\n" + "2. SELECT 1... ResultSet :)\n" + "3. more results?... nope :)\n" + "4. even more results?... nope :)\n"); } private void Test_Wrapper() { sb.setLength(0); // clear the output log buffer try { final String jdbc_pkg = "java.sql."; final String monetdb_jdbc_pkg = "org.monetdb.jdbc."; sb.append("Auto commit is: ").append(con.getAutoCommit()).append("\n"); checkIsWrapperFor("Connection", con, jdbc_pkg, "Connection"); checkIsWrapperFor("Connection", con, monetdb_jdbc_pkg, "MonetConnection"); checkIsWrapperFor("Connection", con, jdbc_pkg, "Statement"); checkIsWrapperFor("Connection", con, monetdb_jdbc_pkg, "MonetStatement"); DatabaseMetaData dbmd = con.getMetaData(); checkIsWrapperFor("DatabaseMetaData", dbmd, jdbc_pkg, "DatabaseMetaData"); checkIsWrapperFor("DatabaseMetaData", dbmd, monetdb_jdbc_pkg, "MonetDatabaseMetaData"); checkIsWrapperFor("DatabaseMetaData", dbmd, jdbc_pkg, "Statement"); checkIsWrapperFor("DatabaseMetaData", dbmd, monetdb_jdbc_pkg, "MonetStatement"); ResultSet rs = dbmd.getSchemas(); checkIsWrapperFor("ResultSet", rs, jdbc_pkg, "ResultSet"); checkIsWrapperFor("ResultSet", rs, monetdb_jdbc_pkg, "MonetResultSet"); checkIsWrapperFor("ResultSet", rs, jdbc_pkg, "Statement"); checkIsWrapperFor("ResultSet", rs, monetdb_jdbc_pkg, "MonetStatement"); ResultSetMetaData rsmd = rs.getMetaData(); checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "ResultSetMetaData"); checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSet"); checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSetMetaData"); checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "Statement"); checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetStatement"); rs.close(); Statement stmt = con.createStatement(); checkIsWrapperFor("Statement", stmt, jdbc_pkg, "Statement"); checkIsWrapperFor("Statement", stmt, monetdb_jdbc_pkg, "MonetStatement"); checkIsWrapperFor("Statement", stmt, jdbc_pkg, "Connection"); checkIsWrapperFor("Statement", stmt, monetdb_jdbc_pkg, "MonetConnection"); stmt.close(); PreparedStatement pstmt = con.prepareStatement("SELECT name FROM sys.tables WHERE system AND name like ?"); checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "PreparedStatement"); checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetPreparedStatement"); checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "Statement"); checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetStatement"); checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "Connection"); checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetConnection"); ParameterMetaData pmd = pstmt.getParameterMetaData(); checkIsWrapperFor("ParameterMetaData", pmd, jdbc_pkg, "ParameterMetaData"); checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetPreparedStatement"); checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetParameterMetaData"); checkIsWrapperFor("ParameterMetaData", pmd, jdbc_pkg, "Connection"); checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetConnection"); ResultSetMetaData psrsmd = pstmt.getMetaData(); checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, jdbc_pkg, "ResultSetMetaData"); checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetPreparedStatement"); checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetResultSetMetaData"); checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, jdbc_pkg, "Connection"); checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetConnection"); pstmt.close(); } catch (SQLException e) { while ((e = e.getNextException()) != null) sb.append("FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_Wrapper", "Auto commit is: true\n" + "Connection. isWrapperFor(Connection) returns: true Called unwrap(). Returned object is not null, so oke\n" + "Connection. isWrapperFor(MonetConnection) returns: true Called unwrap(). Returned object is not null, so oke\n" + "Connection. isWrapperFor(Statement) returns: false\n" + "Connection. isWrapperFor(MonetStatement) returns: false\n" + "DatabaseMetaData. isWrapperFor(DatabaseMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "DatabaseMetaData. isWrapperFor(MonetDatabaseMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "DatabaseMetaData. isWrapperFor(Statement) returns: false\n" + "DatabaseMetaData. isWrapperFor(MonetStatement) returns: false\n" + "ResultSet. isWrapperFor(ResultSet) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ResultSet. isWrapperFor(MonetResultSet) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ResultSet. isWrapperFor(Statement) returns: false\n" + "ResultSet. isWrapperFor(MonetStatement) returns: false\n" + "ResultSetMetaData. isWrapperFor(ResultSetMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ResultSetMetaData. isWrapperFor(MonetResultSet) returns: false\n" + "ResultSetMetaData. isWrapperFor(MonetResultSetMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ResultSetMetaData. isWrapperFor(Statement) returns: false\n" + "ResultSetMetaData. isWrapperFor(MonetStatement) returns: false\n" + "Statement. isWrapperFor(Statement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "Statement. isWrapperFor(MonetStatement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "Statement. isWrapperFor(Connection) returns: false\n" + "Statement. isWrapperFor(MonetConnection) returns: false\n" + "PreparedStatement. isWrapperFor(PreparedStatement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PreparedStatement. isWrapperFor(MonetPreparedStatement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PreparedStatement. isWrapperFor(Statement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PreparedStatement. isWrapperFor(MonetStatement) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PreparedStatement. isWrapperFor(Connection) returns: false\n" + "PreparedStatement. isWrapperFor(MonetConnection) returns: false\n" + "ParameterMetaData. isWrapperFor(ParameterMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ParameterMetaData. isWrapperFor(MonetPreparedStatement) returns: false\n" + "ParameterMetaData. isWrapperFor(MonetParameterMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "ParameterMetaData. isWrapperFor(Connection) returns: false\n" + "ParameterMetaData. isWrapperFor(MonetConnection) returns: false\n" + "PrepStmt ResultSetMetaData. isWrapperFor(ResultSetMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PrepStmt ResultSetMetaData. isWrapperFor(MonetPreparedStatement) returns: false\n" + "PrepStmt ResultSetMetaData. isWrapperFor(MonetResultSetMetaData) returns: true Called unwrap(). Returned object is not null, so oke\n" + "PrepStmt ResultSetMetaData. isWrapperFor(Connection) returns: false\n" + "PrepStmt ResultSetMetaData. isWrapperFor(MonetConnection) returns: false\n"); } private void checkIsWrapperFor(String objnm, Wrapper obj, String pkgnm, String classnm) { try { Class<?> clazz = Class.forName(pkgnm + classnm); boolean isWrapper = obj.isWrapperFor(clazz); sb.append(objnm).append(". isWrapperFor(").append(classnm).append(") returns: ").append(isWrapper); if (isWrapper) { Object wobj = obj.unwrap(clazz); sb.append("\tCalled unwrap(). Returned object is ").append((wobj != null ? "not null, so oke" : "null !!")); } sb.append("\n"); } catch (ClassNotFoundException cnfe) { sb.append(cnfe.toString()); } catch (SQLException se) { sb.append(se.getMessage()); } } private void bogus_auto_generated_keys() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; int upd = 0; try { stmt = con.createStatement(); sb.append("1. create table..."); // create a simple table with an auto-generated key (id) upd = stmt.executeUpdate("CREATE TABLE bogus_gen_keys (\n \"id\" serial,\n \"x\" varchar(12)\n);"); if (upd != Statement.SUCCESS_NO_INFO) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); // perform an update, useless, but illustrates the bug, this time no // generated key is reported, which is correct sb.append("2. update empty table..."); upd = stmt.executeUpdate("UPDATE bogus_gen_keys SET \"x\" = 'bla' WHERE \"id\" = 12;"); if (upd != 0) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); // insert some value, should get a generated key sb.append("3. insert 1 row ..."); upd = stmt.executeUpdate("INSERT INTO bogus_gen_keys (\"x\") VALUES ('boe');"); if (upd != 1) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); sb.append("4. show values of inserted row ..."); rs = stmt.executeQuery("SELECT \"id\", \"x\" from bogus_gen_keys;"); if (rs != null && rs.next()) { sb.append(" id: ").append(rs.getString(1)).append(" x: ").append(rs.getString(2)); } sb.append("\n"); // update again, we expect NO generated key, but we DO get one sb.append("5. update row 1..."); upd = stmt.executeUpdate("UPDATE bogus_gen_keys SET \"x\" = 'bla' WHERE \"id\" = 1;"); if (upd != 1) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); sb.append("6. update row 12..."); upd = stmt.executeUpdate("UPDATE bogus_gen_keys SET \"x\" = 'bla' WHERE \"id\" = 12;"); if (upd != 0) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup try { sb.append("7. drop table..."); upd = stmt.executeUpdate("DROP TABLE bogus_gen_keys"); if (upd != Statement.SUCCESS_NO_INFO) sb.append("Wrong return status: ").append(upd).append("\n"); else sb.append("passed\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("bogus_auto_generated_keys", "1. create table...passed\n" + "2. update empty table...passed\n" + "3. insert 1 row ...passed\n" + "4. show values of inserted row ... id: 1 x: boe\n" + "5. update row 1...passed\n" + "6. update row 12...passed\n" + "7. drop table...passed\n"); } private void BugConcurrent_clients_SF_1504657(String arg0) { sb.setLength(0); // clear the output log buffer Connection con1 = null, con2 = null, con3 = null; Statement stmt1 = null, stmt2 = null, stmt3 = null; ResultSet rs1 = null, rs2= null, rs3 = null; try { con1 = DriverManager.getConnection(arg0); con2 = DriverManager.getConnection(arg0); con3 = DriverManager.getConnection(arg0); stmt1 = con1.createStatement(); stmt2 = con2.createStatement(); stmt3 = con3.createStatement(); // >> true: auto commit should be on by default sb.append("0. true\t").append(con1.getAutoCommit()).append("\n"); sb.append("0. true\t").append(con2.getAutoCommit()).append("\n"); sb.append("0. true\t").append(con3.getAutoCommit()).append("\n"); // test the creation of a table with concurrent clients sb.append("1.1. create table t1504657 using client 1...\n"); stmt1.executeUpdate("CREATE TABLE t1504657 ( id int, name varchar(1024) )"); sb.append("passed :)\n"); sb.append("1.2. check table existence in client 2...\n"); rs2 = stmt2.executeQuery("SELECT name FROM tables where name LIKE 't1504657'"); while (rs2.next()) sb.append(rs2.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("1.3. check table existence in client 3...\n"); rs3 = stmt3.executeQuery("SELECT name FROM tables where name LIKE 't1504657'"); while (rs3.next()) sb.append(rs3.getString("name")).append("\n"); sb.append("passed :)\n"); // test the insertion of values with concurrent clients sb.append("2 insert into t1504657 using client 1...\n"); stmt1.executeUpdate("INSERT INTO t1504657 values( 1, 'monetdb' )"); sb.append("passed :)\n"); stmt1.executeUpdate("INSERT INTO t1504657 values( 2, 'monet' )"); sb.append("passed :)\n"); stmt1.executeUpdate("INSERT INTO t1504657 values( 3, 'mon' )"); sb.append("passed :)\n"); sb.append("2.1. check table status with client 1...\n"); rs1 = stmt1.executeQuery("SELECT * FROM t1504657"); while (rs1.next()) sb.append(rs1.getInt("id")).append(", ").append(rs1.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("2.2. check table status with client 2...\n"); rs2 = stmt2.executeQuery("SELECT * FROM t1504657"); while (rs2.next()) sb.append(rs2.getInt("id")).append(", ").append(rs2.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("2.3. check table status with client 3...\n"); rs3 = stmt3.executeQuery("SELECT * FROM t1504657"); while (rs3.next()) sb.append(rs3.getInt("id")).append(", ").append(rs3.getString("name")).append("\n"); sb.append("passed :)\n"); // test the insertion of values with concurrent clients sb.append("3 insert into t1504657 using client 2...\n"); stmt2.executeUpdate("INSERT INTO t1504657 values( 4, 'monetdb' )"); sb.append("passed :)\n"); stmt2.executeUpdate("INSERT INTO t1504657 values( 5, 'monet' )"); sb.append("passed :)\n"); stmt2.executeUpdate("INSERT INTO t1504657 values( 6, 'mon' )"); sb.append("passed :)\n"); sb.append("3.1. check table status with client 1...\n"); rs1 = stmt1.executeQuery("SELECT * FROM t1504657"); while (rs1.next()) sb.append(rs1.getInt("id")).append(", ").append(rs1.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("3.2. check table status with client 2...\n"); rs2 = stmt2.executeQuery("SELECT * FROM t1504657"); while (rs2.next()) sb.append(rs2.getInt("id")).append(", ").append(rs2.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("3.3. check table status with client 3...\n"); rs3 = stmt3.executeQuery("SELECT * FROM t1504657"); while (rs3.next()) sb.append(rs3.getInt("id")).append(", ").append(rs3.getString("name")).append("\n"); sb.append("passed :)\n"); // test the insertion of values with concurrent clients sb.append("4 insert into t1504657 using client 3...\n"); stmt3.executeUpdate("INSERT INTO t1504657 values( 7, 'monetdb' )"); sb.append("passed :)\n"); stmt3.executeUpdate("INSERT INTO t1504657 values( 8, 'monet' )"); sb.append("passed :)\n"); stmt3.executeUpdate("INSERT INTO t1504657 values( 9, 'mon' )"); sb.append("passed :)\n"); sb.append("4.1. check table status with client 1...\n"); rs1 = stmt1.executeQuery("SELECT * FROM t1504657"); while (rs1.next()) sb.append(rs1.getInt("id")).append(", ").append(rs1.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("4.2. check table status with client 2...\n"); rs2 = stmt2.executeQuery("SELECT * FROM t1504657"); while (rs2.next()) sb.append(rs2.getInt("id")).append(", ").append(rs2.getString("name")).append("\n"); sb.append("passed :)\n"); sb.append("4.3. check table status with client 3...\n"); rs3 = stmt3.executeQuery("SELECT * FROM t1504657"); while (rs3.next()) sb.append(rs3.getInt("id")).append(", ").append(rs3.getString("name")).append("\n"); sb.append("passed :)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup try { sb.append("Cleanup TABLE t1504657\n"); stmt3.executeUpdate("DROP TABLE t1504657"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt3, rs3); closeStmtResSet(stmt1, rs1); closeStmtResSet(stmt2, rs2); closeConx(con2); closeConx(con1); closeConx(con3); compareExpectedOutput("BugConcurrent_clients_SF_1504657", "0. true true\n" + "0. true true\n" + "0. true true\n" + "1.1. create table t1504657 using client 1...\n" + "passed :)\n" + "1.2. check table existence in client 2...\n" + "t1504657\n" + "passed :)\n" + "1.3. check table existence in client 3...\n" + "t1504657\n" + "passed :)\n" + "2 insert into t1504657 using client 1...\n" + "passed :)\n" + "passed :)\n" + "passed :)\n" + "2.1. check table status with client 1...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "passed :)\n" + "2.2. check table status with client 2...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "passed :)\n" + "2.3. check table status with client 3...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "passed :)\n" + "3 insert into t1504657 using client 2...\n" + "passed :)\n" + "passed :)\n" + "passed :)\n" + "3.1. check table status with client 1...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "passed :)\n" + "3.2. check table status with client 2...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "passed :)\n" + "3.3. check table status with client 3...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "passed :)\n" + "4 insert into t1504657 using client 3...\n" + "passed :)\n" + "passed :)\n" + "passed :)\n" + "4.1. check table status with client 1...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "7, monetdb\n" + "8, monet\n" + "9, mon\n" + "passed :)\n" + "4.2. check table status with client 2...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "7, monetdb\n" + "8, monet\n" + "9, mon\n" + "passed :)\n" + "4.3. check table status with client 3...\n" + "1, monetdb\n" + "2, monet\n" + "3, mon\n" + "4, monetdb\n" + "5, monet\n" + "6, mon\n" + "7, monetdb\n" + "8, monet\n" + "9, mon\n" + "passed :)\n" + "Cleanup TABLE t1504657\n"); } private void BugConcurrent_sequences(String arg0) { sb.setLength(0); // clear the output log buffer Connection con1 = null, con2 = null; Statement stmt1 = null, stmt2 = null; ResultSet rs1 = null, rs2 = null; try { con1 = DriverManager.getConnection(arg0); con2 = DriverManager.getConnection(arg0); stmt1 = con1.createStatement(); stmt2 = con2.createStatement(); // >> true: auto commit should be on by default sb.append("0. true\t").append(con1.getAutoCommit()).append("\n"); sb.append("0. true\t").append(con2.getAutoCommit()).append("\n"); // create a table sb.append("1. create table tconc_seq using client 1... "); stmt1.executeUpdate("CREATE TABLE tconc_seq ( id serial, who varchar(12) )"); sb.append("passed :)\n"); // test the insertion of values with concurrent clients sb.append("2. insert into tconc_seq using client 1 and 2... "); stmt1.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client1')"); sb.append("client 1 passed :)\n"); con2.setAutoCommit(false); stmt2.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client2')"); sb.append("transaction on client 2 :)\n"); stmt1.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client1')"); sb.append("client 1 passed :)\n"); try { con2.commit(); sb.append("transaction client 2 passed :)\n"); } catch (SQLException e) { sb.append("transaction client 2 failed!\n"); } con2.setAutoCommit(true); stmt2.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client2')"); sb.append("passed :)\n"); sb.append("2.1. check table status with client 1...\n"); rs1 = stmt1.executeQuery("SELECT * FROM tconc_seq ORDER BY id"); while (rs1.next()) sb.append(rs1.getInt("id")).append(", ").append(rs1.getString("who")).append("\n"); sb.append("passed :)\n"); sb.append("2.2. check table status with client 2...\n"); rs2 = stmt2.executeQuery("SELECT * FROM tconc_seq ORDER BY id"); while (rs2.next()) sb.append(rs2.getInt("id")).append(", ").append(rs2.getString("who")).append("\n"); sb.append("passed :)\n"); // drop the table (not dropping the sequence) from client 1 sb.append("3.1. drop table tconc_seq using client 1... "); stmt1.executeUpdate("DROP TABLE tconc_seq"); sb.append("passed :)\n"); sb.append("3.1. recreate tconc_seq using client 1... "); stmt1.executeUpdate("CREATE TABLE tconc_seq ( id serial, who varchar(12) )"); sb.append("passed :)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("BugConcurrent_sequences", "0. true true\n" + "0. true true\n" + "1. create table tconc_seq using client 1... passed :)\n" + "2. insert into tconc_seq using client 1 and 2... client 1 passed :)\n" + "transaction on client 2 :)\n" + "client 1 passed :)\n" + "transaction client 2 failed!\n" + "passed :)\n" + "2.1. check table status with client 1...\n" + "1, client1\n" + "3, client1\n" + "4, client2\n" + "passed :)\n" + "2.2. check table status with client 2...\n" + "1, client1\n" + "3, client1\n" + "4, client2\n" + "passed :)\n" + "3.1. drop table tconc_seq using client 1... passed :)\n" + "3.1. recreate tconc_seq using client 1... passed :)\n"); sb.setLength(0); // clear the output log buffer try { // re-establish connection sb.append("x. Reconnecting client 1 and 2... "); con1.close(); con2.close(); con1 = DriverManager.getConnection(arg0); con2 = DriverManager.getConnection(arg0); stmt1 = con1.createStatement(); stmt2 = con2.createStatement(); sb.append("passed :)\n"); // insert and print, should get 1,2 sb.append("4. insert into tconc_seq using client 1 and 2...\n"); stmt1.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client1')"); sb.append("passed :)\n"); con2.setAutoCommit(false); stmt2.executeUpdate("INSERT INTO tconc_seq(who) VALUES('client2')"); con2.commit(); con2.setAutoCommit(true); sb.append("passed :)\n"); sb.append("4.1. check table status with client 1...\n"); rs1 = stmt1.executeQuery("SELECT * FROM tconc_seq ORDER BY who"); for (int cntr = 1; rs1.next(); cntr++) { int id = rs1.getInt("id"); sb.append(id).append(", ").append(rs1.getString("who")).append("\n"); if (id != cntr) sb.append("!! expected ").append(cntr).append(", got ").append(id); } sb.append("passed :)\n"); sb.append("4.2. check table status with client 2...\n"); rs2 = stmt2.executeQuery("SELECT * FROM tconc_seq ORDER BY who"); for (int cntr = 1; rs2.next(); cntr++) { int id = rs2.getInt("id"); sb.append(id).append(", ").append(rs2.getString("who")).append("\n"); if (id != cntr) sb.append("!! expected ").append(cntr).append(", got ").append(id); } sb.append("passed :)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup try { sb.append("Cleanup TABLE tconc_seq\n"); stmt2.executeUpdate("DROP TABLE tconc_seq"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt1, rs1); closeStmtResSet(stmt2, rs2); closeConx(con2); closeConx(con1); compareExpectedOutput("BugConcurrent_sequences", "x. Reconnecting client 1 and 2... passed :)\n" + "4. insert into tconc_seq using client 1 and 2...\n" + "passed :)\n" + "passed :)\n" + "4.1. check table status with client 1...\n" + "1, client1\n" + "2, client2\n" + "passed :)\n" + "4.2. check table status with client 2...\n" + "1, client1\n" + "2, client2\n" + "passed :)\n" + "Cleanup TABLE tconc_seq\n"); } private void Bug_Connect_as_voc_getMetaData_Failure_Bug_6388(String arg0) { sb.setLength(0); // clear the output log buffer Statement stmt1 = null; // create user, schema and alter schema default schema try { sb.append("1. CREATE USER voc\n"); stmt1 = con.createStatement(); stmt1.executeUpdate("CREATE USER \"voc\" WITH PASSWORD 'voc' NAME 'VOC Explorer' SCHEMA \"sys\""); sb.append("2. CREATE SCHEMA voc\n"); stmt1.executeUpdate("CREATE SCHEMA \"voc\" AUTHORIZATION \"voc\""); sb.append("3. ALTER USER voc\n"); stmt1.executeUpdate("ALTER USER \"voc\" SET SCHEMA \"voc\""); sb.append("creation succeeded :)\n"); } catch (SQLException e) { sb.append("FAILED creating user and schema voc. ").append(e.getMessage()).append("\n"); } Connection con2 = null; ResultSet rs2 = null; try { sb.append("4.1. connect as user: voc\n"); con2 = DriverManager.getConnection(arg0.replace("=monetdb", "=voc")); sb.append("connected :)\n"); DatabaseMetaData dbmd = con2.getMetaData(); sb.append("4.2. getUserName()\n"); sb.append("UserName = ").append(dbmd.getUserName()).append("\n"); sb.append("4.3. getMaxConnections()\n"); sb.append("MaxConnections = ").append(dbmd.getMaxConnections()).append("\n"); sb.append("4.4. getDatabaseProductVersion()\n"); String dbmsVersion = dbmd.getDatabaseProductVersion(); // should be 11.35.1 or higher boolean postNov2019 = ("11.35.1".compareTo(dbmsVersion) <= 0); sb.append("DatabaseProductVersion = ").append((postNov2019 ? "11.35.+" : dbmsVersion)).append("\n"); sb.append("4.5. getDatabaseMajorVersion()\n"); sb.append("DatabaseMajorVersion = ").append(dbmd.getDatabaseMajorVersion()).append("\n"); // should be 11 sb.append("4.6. getDatabaseMinorVersion()\n"); int dbmsMinorVersion = dbmd.getDatabaseMinorVersion(); // should be 35 or higher sb.append("DatabaseMinorVersion = ").append((dbmsMinorVersion >= 35 ? "35+" : dbmsMinorVersion)).append("\n"); sb.append("4.7. getTables(null, 'tmp', null, null)\n"); rs2 = dbmd.getTables(null, "tmp", null, null); if (rs2 != null) { sb.append("List Tables in schema tmp:\n"); while (rs2.next()) { sb.append(rs2.getString(3)).append("\n"); } rs2.close(); } sb.append("completed listing Tables in schema tmp\n"); sb.append("4.8. getTableTypes()\n"); rs2 = dbmd.getTableTypes(); if (rs2 != null) { sb.append("List TableTypes:\n"); while (rs2.next()) { String tt = rs2.getString(1); // the STREAM TABLE type is REMOVED in post Oct2020 releases, so filter it out for a stable output on all releases // the UNLOGGED TABLE type is ADDED in post Jan2022 releases, so filter it out for a stable output on all releases if (! ("STREAM TABLE".equals(tt) || "UNLOGGED TABLE".equals(tt)) ) sb.append(tt).append("\n"); } rs2.close(); } sb.append("completed listing TableTypes\n"); sb.append("voc meta data Test completed successfully\n"); } catch (SQLException e) { sb.append("FAILED fetching MonetDatabaseMetaData. ").append(e.getMessage()).append("\n"); } finally { try { con2.close(); } catch (SQLException e) { sb.append("FAILED to close voc connection. ").append(e.getMessage()).append("\n"); } } // cleanup: drop user, schema and alter schema default schema in reverse order try { sb.append("Cleanup created objects\n"); sb.append("5. ALTER USER voc\n"); stmt1.executeUpdate("ALTER USER \"voc\" SET SCHEMA \"sys\""); sb.append("6. DROP SCHEMA voc\n"); stmt1.executeUpdate("DROP SCHEMA \"voc\""); sb.append("7. DROP USER voc\n"); stmt1.executeUpdate("DROP USER \"voc\""); sb.append("cleanup succeeded :)\n"); } catch (SQLException e) { sb.append("FAILED dropping user and schema voc. ").append(e.getMessage()).append("\n"); } closeConx(con2); closeStmtResSet(stmt1, null); compareExpectedOutput("Bug_Connect_as_voc_getMetaData_Failure_Bug_6388", "1. CREATE USER voc\n" + "2. CREATE SCHEMA voc\n" + "3. ALTER USER voc\n" + "creation succeeded :)\n" + "4.1. connect as user: voc\n" + "connected :)\n" + "4.2. getUserName()\n" + "UserName = voc\n" + "4.3. getMaxConnections()\n" + "MaxConnections = 64\n" + "4.4. getDatabaseProductVersion()\n" + "DatabaseProductVersion = 11.35.+\n" + "4.5. getDatabaseMajorVersion()\n" + "DatabaseMajorVersion = 11\n" + "4.6. getDatabaseMinorVersion()\n" + "DatabaseMinorVersion = 35+\n" + "4.7. getTables(null, 'tmp', null, null)\n" + "List Tables in schema tmp:\n" + "_columns\n" + "_tables\n" + "idxs\n" + "keys\n" + "objects\n" + "triggers\n" + "completed listing Tables in schema tmp\n" + "4.8. getTableTypes()\n" + "List TableTypes:\n" + "GLOBAL TEMPORARY TABLE\n" + "LOCAL TEMPORARY TABLE\n" + "MERGE TABLE\n" + "REMOTE TABLE\n" + "REPLICA TABLE\n" + "SYSTEM TABLE\n" + "SYSTEM VIEW\n" + "TABLE\n" + "VIEW\n" + "completed listing TableTypes\n" + "voc meta data Test completed successfully\n" + "Cleanup created objects\n" + "5. ALTER USER voc\n" + "6. DROP SCHEMA voc\n" + "7. DROP USER voc\n" + "cleanup succeeded :)\n"); } private void BugDatabaseMetaData_Bug_3356() { sb.setLength(0); // clear the output log buffer ResultSet rs = null; try { DatabaseMetaData dbmd = con.getMetaData(); rs = dbmd.getColumns("", "sys", "_tables", "id"); rs.next(); String tableName1 = rs.getString("TABLE_NAME"); String tableName2 = rs.getString(3); String isNullable1 = rs.getString("IS_NULLABLE"); String isNullable2 = rs.getString(18); sb.append(tableName1).append("\n"); sb.append(tableName2).append("\n"); sb.append(isNullable1).append("\n"); sb.append(isNullable2).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(null, rs); compareExpectedOutput("BugDatabaseMetaData_Bug_3356", "_tables\n" + "_tables\n" + "YES\n" + "YES\n"); } private void BugDecimalRound_Bug_3561() { sb.setLength(0); // clear the output log buffer Statement stmt1 = null; PreparedStatement pst = null; Statement stmt2 = null; ResultSet rs = null; try { stmt1 = con.createStatement(); stmt1.executeUpdate("CREATE TABLE bug3561 (d decimal(7,4))"); pst = con.prepareStatement("INSERT INTO bug3561 VALUES (?)"); pst.setBigDecimal(1, new BigDecimal("112.125")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal("212.12345")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal("0.012345")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(0.0/10000000)); // 0.0000 pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(2.0/3)); // 0.666666667 pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(11.0/7)); // 1.571428571 pst.executeUpdate(); // repeat for negative values pst.setBigDecimal(1, new BigDecimal("-0112.125")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal("-0212.12345")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal("-0.012345")); pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(0.0/-10000000)); // 0.0000 pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(-2.0/3)); // -0.666666667 pst.executeUpdate(); pst.setBigDecimal(1, new BigDecimal(-11.0/7)); // -1.571428571 pst.executeUpdate(); // check what happens if null is used pst.setBigDecimal(1, null); pst.executeUpdate(); pst.close(); stmt2 = con.createStatement(); rs = stmt2.executeQuery("SELECT d FROM bug3561"); while (rs.next()) sb.append(rs.getString(1)).append("\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt2, rs); closeStmtResSet(pst, null); // cleanup try { stmt1.executeUpdate("DROP TABLE bug3561"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt1, null); compareExpectedOutput("BugDecimalRound_Bug_3561", "112.1250\n" + "212.1235\n" + "0.0123\n" + "0.0000\n" + "0.6667\n" + "1.5714\n" + "-112.1250\n" + "-212.1235\n" + "-0.0123\n" + "0.0000\n" + "-0.6667\n" + "-1.5714\n" + "null\n"); } private void BugExecuteUpdate_Bug_3350() { sb.setLength(0); // clear the output log buffer Statement stmt = null; try { con.setAutoCommit(false); // disable auto commit, so we can roll back the transaction stmt = con.createStatement(); stmt.execute("CREATE TABLE t3350 (keyword VARCHAR(30) PRIMARY KEY)"); con.commit(); executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // should insert 1 row executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // this will result in an SQLException due to PK uniqueness violation con.rollback(); executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // should insert 1 row executeDML(stmt, "INSERT INTO t3350 VALUES ('1'), ('x'), ('3'), ('y')"); // should insert 4 rows executeDML(stmt, "DELETE FROM t3350 WHERE \"keyword\" = 'Bug_3350'"); // should delete 1 row executeDML(stmt, "DELETE FROM t3350 WHERE \"keyword\" = 'Bug_3350'"); // should delete 0 rows executeDML(stmt, "UPDATE t3350 set \"keyword\" = keyword||'_ext'"); // should update 4 rows executeDML(stmt, "DELETE FROM t3350"); // should delete 4 rows con.commit(); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } closeStmtResSet(stmt, null); // cleanup try { stmt.execute("DROP TABLE IF EXISTS t3350"); con.commit(); con.setAutoCommit(true); // enable auto commit } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("BugExecuteUpdate_Bug_3350", "executeUpdate(INSERT ...) returned: 1\n" + "getUpdateCount() returned: 1\n" + "INSERT INTO: PRIMARY KEY constraint 't3350.t3350_keyword_pkey' violated\n" + "getUpdateCount() returned: 1\n" + "executeUpdate(INSERT ...) returned: 1\n" + "getUpdateCount() returned: 1\n" + "executeUpdate(INSERT ...) returned: 4\n" + "getUpdateCount() returned: 4\n" + "executeUpdate(DELETE ...) returned: 1\n" + "getUpdateCount() returned: 1\n" + "executeUpdate(DELETE ...) returned: 0\n" + "getUpdateCount() returned: 0\n" + "executeUpdate(UPDATE ...) returned: 4\n" + "getUpdateCount() returned: 4\n" + "executeUpdate(DELETE ...) returned: 4\n" + "getUpdateCount() returned: 4\n"); } private void executeDML(Statement st, String sql) { try { int upd_count = st.executeUpdate(sql); sb.append("executeUpdate(").append(sql.substring(0, 6)).append(" ...) returned: ").append(upd_count).append("\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } try { sb.append("getUpdateCount() returned: ").append(st.getUpdateCount()).append("\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } } private void Bug_IsValid_Timeout_Bug_6782(String arg0) { sb.setLength(0); // clear the output log buffer Connection con2 = null; Statement st = null; try { con2 = DriverManager.getConnection(arg0); sb.append("connected :)\n"); st = con2.createStatement(); st.setQueryTimeout(5); sb.append("getQueryTimeout must give 5: ").append(st.getQueryTimeout()).append("\n"); st.close(); con.isValid(6); st = con.createStatement(); sb.append("getQueryTimeout must give 0: ").append(st.getQueryTimeout()).append("\n"); con.isValid(4); sb.append("getQueryTimeout must give 0: ").append(st.getQueryTimeout()).append("\n"); st.close(); st.setQueryTimeout(7); con.isValid(3); sb.append("getQueryTimeout must give 7: ").append(st.getQueryTimeout()).append("\n"); st.close(); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } closeStmtResSet(st, null); closeConx(con2); compareExpectedOutput("Bug_IsValid_Timeout_Bug_6782", "connected :)\n" + "getQueryTimeout must give 5: 5\n" + "getQueryTimeout must give 0: 0\n" + "getQueryTimeout must give 0: 0\n" + "getQueryTimeout must give 7: 7\n"); } private void Bug_LargeQueries_6571_6693(String arg0) { sb.setLength(0); // clear the output log buffer // construct a largedata string value. It must larger than the block size of MapiSocket final int num = 9216; final String repeatValue = "$-)"; final StringBuilder ldsb = new StringBuilder(num * repeatValue.length()); for (int i = 0; i < num; i++) ldsb.append(repeatValue); final String largedata = ldsb.toString(); if (largedata.length() <= 8192) sb.append("Length (").append(largedata.length()).append(") of largedata value is too small! Should be larger than 8192!"); final String tbl_nm = "tbl6693"; Statement stmt = null; try { stmt = con.createStatement(); // create a test table. stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + tbl_nm + " (attribute CLOB, value CLOB);"); sb.append("Created table: ").append(tbl_nm).append("\n"); sb.append("Inserting rows. "); String insertCmd = "INSERT INTO " + tbl_nm + " VALUES ('activeset_default_fiets', '" + largedata + "');"; int ins = stmt.executeUpdate(insertCmd); ins += stmt.executeUpdate(insertCmd); ins += stmt.executeUpdate(insertCmd); sb.append(ins).append(" rows inserted\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } closeStmtResSet(stmt, null); final int script_iterations = 10; try { run_tests(arg0, tbl_nm, script_iterations, largedata); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } try (Statement stmt2 = con.createStatement()) { stmt2.executeUpdate("DROP TABLE IF EXISTS " + tbl_nm); sb.append("Cleaned up TABLE ").append(tbl_nm).append("\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } sb.append("Test completed without hanging\n"); compareExpectedOutput("Bug_LargeQueries_6571_6693", "Created table: tbl6693\n" + "Inserting rows. 3 rows inserted\n" + "Script size is 83256\n" + "First test repeat 10 times. Iteration: 1 2 3 4 5 6 7 8 9 10 \n" + "Completed first test\n" + "Second test repeat 10 times. Iteration: 1 2 3 4 5 6 7 8 9 10 \n" + "Completed second test\n" + "Script size is 3012\n" + "Third test repeat 9 times.\n" + "Iteration: 1 2 3 4 5 6 7 8 9 \n" + "Completed third test\n" + "Cleaned up TABLE tbl6693\n" + "Test completed without hanging\n"); } private void run_tests(String conURL, String tbl_nm, int iterations, String largedata) throws SQLException { String script = "delete from " + tbl_nm + " where attribute='activeset_default_fiets';\n" + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + "select value from " + tbl_nm + " where attribute='activeset_default_fiets';\n"; sb.append("Script size is " + script.length()).append("\n"); // first try to make the execution hang after many iterations of sending large data queries within one connection sb.append("First test repeat " + iterations + " times. "); try (Connection con = DriverManager.getConnection(conURL)) { sb.append("Iteration: "); for (int i = 1; i <= iterations; i++) { sb.append(i).append(" "); try (Statement stmt = con.createStatement()) { process_script(stmt, script, 1, 3, 6); } } sb.append("\n"); } sb.append("Completed first test\n"); // also try to make the execution hang after many iterations of making connections (each their own socket) and sending large scripts sb.append("Second test repeat " + iterations + " times. "); sb.append("Iteration: "); for (int i = 1; i <= iterations; i++) { try (Connection con = DriverManager.getConnection(conURL)) { sb.append(i).append(" "); try (Statement stmt = con.createStatement()) { process_script(stmt, script, 1, 3, 6); process_script(stmt, script, 1, 3, 6); process_script(stmt, script, 1, 3, 6); process_script(stmt, script, 1, 3, 6); } } } sb.append("\n"); sb.append("Completed second test\n"); // next try to make the execution hang by sending very many queries combined in 1 large script final int queries = 260; StringBuilder qry = new StringBuilder(queries * 13); for (int i = 1; i <= queries; i++) qry.append(" SELECT ").append(i).append(';'); script = qry.toString(); sb.append("Script size is " + script.length()).append("\n"); iterations = 9; sb.append("Third test repeat " + iterations + " times.\n"); try (Connection con = DriverManager.getConnection(conURL)) { sb.append("Iteration: "); for (int i = 1; i <= iterations; i++) { sb.append(i).append(" "); try (Statement stmt = con.createStatement()) { process_script(stmt, script, queries, queries, 0); } } sb.append("\n"); } sb.append("Completed third test\n"); } private void process_script(Statement stmt, String script, int expectedResults, int expectedTotalRows, int expectedUpdates) throws SQLException { int results = 0; int rows = 0; int updates = 0; stmt.execute(script); do { ResultSet rs = stmt.getResultSet(); if (rs != null) { results++; while(rs.next()) { String val = rs.getString(1); rows++; } rs.close(); } else { int uc = stmt.getUpdateCount(); if (uc > 0) updates += uc; } } while (stmt.getMoreResults() || stmt.getUpdateCount() != -1); /* verify nr of processed resultsets and retrieved rows are as expected */ if (results != expectedResults) sb.append(results + "!=" + expectedResults + " "); if (rows != expectedTotalRows) sb.append(rows + "!=" + expectedTotalRows + " "); if (updates != expectedUpdates) sb.append(updates + "!=" + expectedUpdates + " "); } private void Bug_PrepStmtSetObject_CLOB_6349() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE PrepStmtSetObject_CLOB (myint INT, myvarchar VARCHAR(15), myclob CLOB)"); stmt.executeUpdate("INSERT INTO PrepStmtSetObject_CLOB VALUES (123, 'A string', 'A longer string')"); stmt.executeUpdate("INSERT INTO PrepStmtSetObject_CLOB VALUES (NULL, NULL, NULL)"); // all NULLs pstmt = con.prepareStatement("SELECT myclob, myvarchar, myint FROM PrepStmtSetObject_CLOB WHERE myclob = ?"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append("Prepared Query has ").append(pmd.getParameterCount()).append(" parameters. Type of first is: ").append(pmd.getParameterTypeName(1)).append("\n"); ResultSetMetaData rsmd = pstmt.getMetaData(); sb.append("Prepared Query has ").append(rsmd.getColumnCount()).append(" columns. Type of first is: ").append(rsmd.getColumnTypeName(1)).append("\n"); pstmt.setObject(1, "A longer string"); rs = pstmt.executeQuery(); rsmd = rs.getMetaData(); sb.append("Query ResultSet has ").append(rsmd.getColumnCount()).append(" columns. Type of first is: ").append(rsmd.getColumnTypeName(1)).append("\n"); boolean has_row = rs.next(); boolean has_rows = rs.next(); if (has_row == false || has_rows == true) sb.append("Fetching Query ResultSet failed\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } // cleanup try { stmt.executeUpdate("DROP TABLE PrepStmtSetObject_CLOB"); sb.append("Table dropped\n"); } catch (SQLException se) { sb.append(se.getMessage()).append("\n"); } closeStmtResSet(stmt, null); closeStmtResSet(pstmt, rs); compareExpectedOutput("Bug_PrepStmtSetObject_CLOB_6349", "0. true true\n" + "Prepared Query has 1 parameters. Type of first is: " + (isPostDec2023 ? "varchar" : "clob") + "\n" + "Prepared Query has 3 columns. Type of first is: " + (isPostDec2023 ? "varchar" : "clob") + "\n" + "Query ResultSet has 3 columns. Type of first is: " + (isPostDec2023 ? "varchar" : "clob") + "\n" + "Table dropped\n"); } private void Bug_PrepStmtSetString_6382() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; final String tableName = "PrepStmtSetString_6382"; try { // >> true: auto commit should be on by default sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); stmt = con.createStatement(); sb.append("1. Creating table ").append(tableName); if (stmt.executeUpdate("CREATE TABLE " + tableName + " (myint INT, myvarchar VARCHAR(15), myjson JSON, myuuid UUID, myurl URL, myinet INET)") != Statement.SUCCESS_NO_INFO) sb.append("Wrong return status\n"); sb.append("\n2. Insert row 1, "); stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (1, 'row 1', '{}', uuid '34c8deb5-e608-406b-beda-6a951f73d455', 'https://www.monetdb.org/', '128.0.0.1')"); sb.append("2, "); stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (2, 'row 2', '[]', NULL, NULL, NULL)"); sb.append("3, "); stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (3, 'row 3', '\"abc\"', NULL, NULL, NULL)"); sb.append("4, "); stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (4, 'row 4', 'true', NULL, NULL, NULL)"); sb.append("5\n"); stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (5, 'row 5', '-0.123', NULL, NULL, NULL)"); sb.append("Creating a prepared statement with 6 parameters and inserting rows using setInt(), setString(), setNull(), setNString(), setURL(), setObject().\n"); pstmt = con.prepareStatement("INSERT INTO " + tableName + " VALUES (?,?, ? ,?,? , ?)"); ParameterMetaData pmd = pstmt.getParameterMetaData(); int pcount = pmd.getParameterCount(); sb.append("Prepared Statement has ").append(pcount).append(" parameters:").append((pcount != 6 ? " ERROR: Expected 6 parameters!" : "")).append("\n"); for (int p = 1; p <= pcount; p++) { sb.append(" Parameter ").append(p).append(" type is: ").append(pmd.getParameterTypeName(p)).append(". JDBC SQL type: ").append(pmd.getParameterType(p)).append("\n"); } int row = 6; pstmt.setInt(1, row); pstmt.setString(2, "row " + row); pstmt.setString(3, "{\"menu\": {\n \"id\": \"file\",\n \"value\": \"File\",\n \"popup\": {\n \"menuitem\": [\n {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n ]\n }\n}}"); pstmt.setNull(4, 0); pstmt.setNull(5, 0); pstmt.setNull(6, 0); sb.append("Inserting row ").append(row).append("\n"); int inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); row++; // row 7 pstmt.setShort(1, (short)row); pstmt.setNString(2, "row " + row); pstmt.setNull(3, 0); pstmt.setString(4, "4a148b7d-8d47-4e1e-a21e-09a71abf2215"); sb.append("Inserting row ").append(row).append("\n"); inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); row++; // row 8 pstmt.setLong(1, (long)row); pstmt.setString(2, "row " + row); pstmt.setObject(3, "[3.1415E-06]"); pstmt.setNull(4, 0); try { pstmt.setURL(5, new java.net.URI("https://www.cwi.nl/").toURL()); } catch (java.net.URISyntaxException | java.net.MalformedURLException mfe) { sb.append(mfe).append("\n"); } sb.append("Inserting row ").append(row).append("\n"); inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); row++; // row 9 pstmt.setBigDecimal(1, new java.math.BigDecimal(row)); pstmt.setNString(2, "row " + row); pstmt.setNull(5, 0); pstmt.setString(6, "127.255.255.255"); sb.append("Inserting row ").append(row).append("\n"); inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); /* also test generic setObject(int, String) */ row++; // row 10 pstmt.setObject(1, Integer.valueOf(row)); pstmt.setObject(2, "row " + row); pstmt.setObject(3, "[{\"menu\": {\n \"header\": \"SVG Viewer\",\n \"items\": [\n {\"id\": \"Open\"},\n {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n null,\n {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n null,\n {\"id\": \"Quality\"},\n {\"id\": \"Pause\"},\n {\"id\": \"Mute\"},\n null,\n {\"id\": \"Help\"},\n {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n ]\n}}]"); pstmt.setObject(4, "b39dc76e-4faf-4fd9-bc1e-17df48acf764"); pstmt.setObject(5, "https://en.wikipedia.org/wiki/IP_address"); pstmt.setObject(6, "223.255.255.255"); sb.append("Inserting row ").append(row).append("\n"); inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); row++; // row 11 pstmt.setObject(1, new java.math.BigDecimal(row)); pstmt.setObject(2, "row " + row); pstmt.setObject(3, "null"); pstmt.setObject(4, java.util.UUID.fromString("ff125769-b63c-4c3c-859f-5b84a9349e24")); URL myURL = new URL(); try { myURL.fromString("https://en.wikipedia.org/wiki/IP_address"); pstmt.setObject(5, myURL); } catch (Exception mfe) { sb.append(mfe).append("\n"); } INET myINET = new INET(); myINET.fromString("223.234.245.255"); pstmt.setObject(6, myINET); sb.append("Inserting row ").append(row).append("\n"); inserted = pstmt.executeUpdate(); sb.append("Inserted ").append(inserted).append(" row\n"); sb.append("List contents of TABLE ").append(tableName).append(" after ").append(row).append(" rows inserted\n"); rs = stmt.executeQuery("SELECT * FROM " + tableName + " ORDER BY 1"); ResultSetMetaData rsmd = rs.getMetaData(); int colcount = rsmd.getColumnCount(); sb.append("Query has ").append(colcount).append(" output columns.").append((colcount != 6 ? " ERROR: Expected 6 columns!" : "")).append("\n"); row = 0; while (rs.next()) { sb.append("row ").append(++row); for (int c = 1; c <= colcount; c++) { sb.append("\t").append(rs.getString(c)); } sb.append("\n"); } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } try { sb.append("Cleanup TABLE ").append(tableName).append("\n"); stmt.executeUpdate("DROP TABLE " + tableName); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); closeStmtResSet(pstmt, null); compareExpectedOutput("Bug_PrepStmtSetString_6382", /* servers Jun2023 (11.47) and older stored JSON string values as provided */ (dbmsMajorVersion == 11 && dbmsMinorVersion <= 47) ? "0. true true\n" + "1. Creating table PrepStmtSetString_6382\n" + "2. Insert row 1, 2, 3, 4, 5\n" + "Creating a prepared statement with 6 parameters and inserting rows using setInt(), setString(), setNull(), setNString(), setURL(), setObject().\n" + "Prepared Statement has 6 parameters:\n" + " Parameter 1 type is: int. JDBC SQL type: 4\n" + " Parameter 2 type is: varchar. JDBC SQL type: 12\n" + " Parameter 3 type is: json. JDBC SQL type: 12\n" + " Parameter 4 type is: uuid. JDBC SQL type: 12\n" + " Parameter 5 type is: url. JDBC SQL type: 12\n" + " Parameter 6 type is: inet. JDBC SQL type: 12\n" + "Inserting row 6\n" + "Inserted 1 row\n" + "Inserting row 7\n" + "Inserted 1 row\n" + "Inserting row 8\n" + "Inserted 1 row\n" + "Inserting row 9\n" + "Inserted 1 row\n" + "Inserting row 10\n" + "Inserted 1 row\n" + "Inserting row 11\n" + "Inserted 1 row\n" + "List contents of TABLE PrepStmtSetString_6382 after 11 rows inserted\n" + "Query has 6 output columns.\n" + "row 1 1 row 1 {} 34c8deb5-e608-406b-beda-6a951f73d455 https://www.monetdb.org/ 128.0.0.1\n" + "row 2 2 row 2 [] null null null\n" + "row 3 3 row 3 \"abc\" null null null\n" + "row 4 4 row 4 true null null null\n" + "row 5 5 row 5 -0.123 null null null\n" + "row 6 6 row 6 {\"menu\": {\n" + " \"id\": \"file\",\n" + " \"value\": \"File\",\n" + " \"popup\": {\n" + " \"menuitem\": [\n" + " {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n" + " {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n" + " {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n" + " ]\n" + " }\n" + "}} null null null\n" + "row 7 7 row 7 null 4a148b7d-8d47-4e1e-a21e-09a71abf2215 null null\n" + "row 8 8 row 8 [3.1415E-06] null https://www.cwi.nl/ null\n" + "row 9 9 row 9 [3.1415E-06] null null 127.255.255.255\n" + "row 10 10 row 10 [{\"menu\": {\n" + " \"header\": \"SVG Viewer\",\n" + " \"items\": [\n" + " {\"id\": \"Open\"},\n" + " {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n" + " null,\n" + " {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n" + " {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n" + " {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n" + " null,\n" + " {\"id\": \"Quality\"},\n" + " {\"id\": \"Pause\"},\n" + " {\"id\": \"Mute\"},\n" + " null,\n" + " {\"id\": \"Help\"},\n" + " {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n" + " ]\n" + "}}] b39dc76e-4faf-4fd9-bc1e-17df48acf764 https://en.wikipedia.org/wiki/IP_address 223.255.255.255\n" + "row 11 11 row 11 null ff125769-b63c-4c3c-859f-5b84a9349e24 https://en.wikipedia.org/wiki/IP_address 223.234.245.255\n" + "Cleanup TABLE PrepStmtSetString_6382\n" : /* for servers 11.48 and higher, JSON string values are stored in optimized form (without whitespace characters) */ "0. true true\n" + "1. Creating table PrepStmtSetString_6382\n" + "2. Insert row 1, 2, 3, 4, 5\n" + "Creating a prepared statement with 6 parameters and inserting rows using setInt(), setString(), setNull(), setNString(), setURL(), setObject().\n" + "Prepared Statement has 6 parameters:\n" + " Parameter 1 type is: int. JDBC SQL type: 4\n" + " Parameter 2 type is: varchar. JDBC SQL type: 12\n" + " Parameter 3 type is: json. JDBC SQL type: 12\n" + " Parameter 4 type is: uuid. JDBC SQL type: 12\n" + " Parameter 5 type is: url. JDBC SQL type: 12\n" + " Parameter 6 type is: inet. JDBC SQL type: 12\n" + "Inserting row 6\n" + "Inserted 1 row\n" + "Inserting row 7\n" + "Inserted 1 row\n" + "Inserting row 8\n" + "Inserted 1 row\n" + "Inserting row 9\n" + "Inserted 1 row\n" + "Inserting row 10\n" + "Inserted 1 row\n" + "Inserting row 11\n" + "Inserted 1 row\n" + "List contents of TABLE PrepStmtSetString_6382 after 11 rows inserted\n" + "Query has 6 output columns.\n" + "row 1 1 row 1 {} 34c8deb5-e608-406b-beda-6a951f73d455 https://www.monetdb.org/ 128.0.0.1\n" + "row 2 2 row 2 [] null null null\n" + "row 3 3 row 3 \"abc\" null null null\n" + "row 4 4 row 4 true null null null\n" + "row 5 5 row 5 -0.123 null null null\n" + "row 6 6 row 6 {\"menu\":{\"id\":\"file\",\"value\":\"File\",\"popup\":{\"menuitem\":[{\"value\":\"New\",\"onclick\":\"CreateNewDoc()\"},{\"value\":\"Open\",\"onclick\":\"OpenDoc()\"},{\"value\":\"Close\",\"onclick\":\"CloseDoc()\"}]}}} null null null\n" + "row 7 7 row 7 null 4a148b7d-8d47-4e1e-a21e-09a71abf2215 null null\n" + "row 8 8 row 8 [3.1415E-06] null https://www.cwi.nl/ null\n" + "row 9 9 row 9 [3.1415E-06] null null 127.255.255.255\n" + "row 10 10 row 10 [{\"menu\":{\"header\":\"SVG Viewer\",\"items\":[{\"id\":\"Open\"},{\"id\":\"OpenNew\",\"label\":\"Open New\"},null,{\"id\":\"ZoomIn\",\"label\":\"Zoom In\"},{\"id\":\"ZoomOut\",\"label\":\"Zoom Out\"},{\"id\":\"OriginalView\",\"label\":\"Original View\"},null,{\"id\":\"Quality\"},{\"id\":\"Pause\"},{\"id\":\"Mute\"},null,{\"id\":\"Help\"},{\"id\":\"About\",\"label\":\"About Adobe CVG Viewer...\"}]}}] b39dc76e-4faf-4fd9-bc1e-17df48acf764 https://en.wikipedia.org/wiki/IP_address 223.255.255.255\n" + "row 11 11 row 11 null ff125769-b63c-4c3c-859f-5b84a9349e24 https://en.wikipedia.org/wiki/IP_address 223.234.245.255\n" + "Cleanup TABLE PrepStmtSetString_6382\n"); } private void Bug_PrepStmt_With_Errors_Jira292() { sb.setLength(0); // clear the output log buffer Statement stmt = null; PreparedStatement pstmt = null; ResultSet rs = null; try { sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); con.setNetworkTimeout(null, (10 *1000)); stmt = con.createStatement(); stmt.executeUpdate("drop table if exists abacus;"); stmt.executeUpdate("create table abacus ( \"'Zeitachse'\" date,\"'Abo_ID'\" int,\"'description'\" varchar(256),\"'Klassierungs-Typ'\" clob,\"'KlassierungApplikation'\" clob,\"'EP Netto'\" decimal,\"'Nettoumsatz'\" decimal,\"'validfrom'\" date,\"'validuntil'\" date,\"'Abo_aufgeschaltet'\" int,\"'Abo_deaktiviert'\" int,\"'Differenz'\" decimal,\"'User_ID'\" int,\"'UserName'\" varchar(256),\"'client'\" varchar(256),\"'Installations_ID'\" int,\"'InstallationsName'\" varchar(256),\"'Installationsprovider_ID'\" int,\"'InstallationsproviderName'\" varchar(256),\"'INR'\" bigint,\"'NAME'\" varchar(256),\"'PLZ'\" varchar(256),\"'ORT'\" varchar(256),\"'STAAT'\" varchar(256),\"'Reseller_ID'\" int,\"'ResellerName'\" varchar(256),\"'ET_ABO'\" clob,\"'UserName_1'\" varchar(256),\"'Anzahl_Abos'\" decimal,\"'Anzahl_User'\" decimal,\"'Jahr'\" decimal,\"'Monat'\" decimal,\"'Jahr_Monat'\" clob,\"'IFJ'\" clob,\"'RECNUM$'\" int,\"'InlineCalc_Year_Zeitachse'\" int);"); stmt.executeUpdate("insert into abacus values ('2019-10-30',2239,'description','Klassierungs-Typ','Klassierung-Applikation',73.28,68.29,'2018-01-01','2018-12-01',563,63,56.3,852,'UserName','client',134,'InstallationsName',892,'InstallationsproviderName',9348,'NAME','PLZ','ORT','STAAT',934,'ResellerName','ET_ABO','UserName_1',849.2739,1742.718,395.824,39.824,'Jahr_Monat','IFJ',395824,3789);"); sb.append("1. table created and inserted 1 row\n"); String qry = "SELECT \"'ResellerName'\" FROM abacus WHERE ( ( (\"'InstallationsproviderName'\"='Bienz Pius Treuhand- und Revisions AG')) AND ( (\"'validuntil'\"='2018-01-01' AND \"'description'\"='ABEA 2' AND (EXTRACT(YEAR FROM \"'Zeitachse'\")*100 + EXTRACT(MONTH FROM \"'Zeitachse'\"))/100.0='2019.010' AND \"'UserName'\"='AL - Astrid Lincke (Delphys)' AND \"'validfrom'\"='2016-12-01')) AND ( (\"'IFJ'\"='ohne IFJ')) AND ( (\"'InlineCalc_Year_Zeitachse'\"='2019'))) GROUP BY \"'ResellerName'\" LIMIT 1001 OFFSET 0;"; try { sb.append("2. before select query execution\n"); rs = stmt.executeQuery(qry); sb.append("2a. select query executed\n"); if (rs != null) { if (rs.next()) { sb.append("2b. select query returned: " + rs.getString(1)).append("\n"); } rs.close(); rs = null; sb.append("2c. closed select query resultset\n"); } sb.append("2d. normal end of select query\n"); } catch (SQLException se) { sb.append("select query Exception: "+ se.getMessage()).append("\n"); while ((se = se.getNextException()) != null) sb.append("next Exception: "+ se.getMessage()).append("\n"); } try { sb.append("3. before creating a prepared select query\n"); pstmt = con.prepareStatement(qry); sb.append("3a. prepared select query\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append("3b. Prepared Query has " + pmd.getParameterCount() + " parameters."); // "Type of first is: " + pmd.getParameterTypeName(1)).append("\n"); ResultSetMetaData rsmd = pstmt.getMetaData(); sb.append("3c. Prepared Query has " + rsmd.getColumnCount() + " columns. Type of first is: " + rsmd.getColumnTypeName(1)).append("\n"); sb.append("3d. before executing the prepared select query\n"); rs = pstmt.executeQuery(); sb.append("3e. prepared select query executed\n"); if (rs != null) { rsmd = rs.getMetaData(); sb.append("3f. prepared Query ResultSet has " + rsmd.getColumnCount() + " columns. Type of first is: " + rsmd.getColumnTypeName(1)).append("\n"); if (rs.next()) { sb.append("3g. prepared select query returned: " + rs.getString(1)).append("\n"); } rs.close(); rs = null; sb.append("3h. closed prepared select query resultset\n"); } sb.append("3i. normal end of prepared select query\n"); } catch (SQLException se) { sb.append("prepared select query Exception: "+ se.getMessage()).append("\n"); while ((se = se.getNextException()) != null) sb.append("next Exception: "+ se.getMessage()).append("\n"); } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } try { sb.append("4. drop table\n"); stmt.executeUpdate("drop table abacus"); sb.append("5. normal end of test\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); closeStmtResSet(stmt, null); compareExpectedOutput("Bug_PrepStmt_With_Errors_Jira292", "0. true true\n" + "1. table created and inserted 1 row\n" + "2. before select query execution\n" + "2a. select query executed\n" + "2c. closed select query resultset\n" + "2d. normal end of select query\n" + "3. before creating a prepared select query\n" + "3a. prepared select query\n" + "3b. Prepared Query has 0 parameters.3c. Prepared Query has 1 columns. Type of first is: varchar\n" + "3d. before executing the prepared select query\n" + "3e. prepared select query executed\n" + "3f. prepared Query ResultSet has 1 columns. Type of first is: varchar\n" + "3h. closed prepared select query resultset\n" + "3i. normal end of prepared select query\n" + "4. drop table\n" + "5. normal end of test\n"); } private void BugResultSetMetaData_Bug_6183() { sb.setLength(0); // clear the output log buffer final String dqTblName = "\"my dq_table\""; final String[] dqColNames = { "\"my space\"", "\"my, comma_space\"", "\"my$dollar\"", "\"my#hash\"", "\"my tab\"" , "\"my ,tab_comma\"", "\"my, comma_tab\"", "\"my\"\"double_doublequote\"", "\"Abc\"", "\" \"", "\"123\"" }; Statement stmt = null; ResultSet rs = null; try { StringBuilder ctsb = new StringBuilder(30 + (dqColNames.length * (30 + 15))); ctsb.append("CREATE TABLE ").append(dqTblName).append(" ("); for (int n = 0; n < dqColNames.length; n++) { ctsb.append(dqColNames[n]); ctsb.append(" varchar(").append(31 + n).append(')'); if (n < (dqColNames.length -1)) ctsb.append(", "); } ctsb.append(')'); stmt = con.createStatement(); sb.append("1. create table ").append(dqTblName).append("\n"); int ret = stmt.executeUpdate(ctsb.toString()); if (ret != -2) sb.append(" returned: ").append(ret).append(" (expected -2)\n"); String tblName = dqTblName.substring(1, dqTblName.length() -1); // trim the leading and trailing double quote characters sb.append("2. show column names of this new table (").append(tblName).append(") via sys.columns query\n"); rs = stmt.executeQuery("SELECT number, name, type from sys.columns where table_id in (select id from sys._tables where name = '" + tblName + "') order by number"); showResultAndClose_6183(rs); sb.append("3. insert 1 row of data with values same as column names\n"); ctsb.setLength(0); ctsb.append("INSERT INTO ").append(dqTblName).append(" VALUES ("); for (int n = 0; n < dqColNames.length; n++) { ctsb.append('\''); ctsb.append(dqColNames[n]); ctsb.append('\''); if (n < (dqColNames.length -1)) ctsb.append(", "); } ctsb.append(')'); ret = stmt.executeUpdate(ctsb.toString()); if (ret != 1) sb.append(" returned: ").append(ret).append(" (expected 1)\n"); sb.append("4. insert 1 row of data with values same as column names but without enclosing double quotes\n"); ctsb.setLength(0); ctsb.append("INSERT INTO ").append(dqTblName).append(" VALUES ("); for (int n = 0; n < dqColNames.length; n++) { ctsb.append('\''); // remove enclosing double quotes ctsb.append(dqColNames[n].substring(1, dqColNames[n].length() -1)); ctsb.append('\''); if (n < (dqColNames.length -1)) ctsb.append(", "); } ctsb.append(')'); ret = stmt.executeUpdate(ctsb.toString()); if (ret != 1) sb.append(" returned: ").append(ret).append(" (expected 1)\n"); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "1. create table \"my dq_table\"\n" + "2. show column names of this new table (my dq_table) via sys.columns query\n" + "Resultset with 3 columns\n" + " Column Name, Column Label:\n" + "1 number number\n" + "2 name name\n" + "3 type type\n" + "Data rows:\n" + "0 my space varchar\n" + "1 my, comma_space varchar\n" + "2 my$dollar varchar\n" + "3 my#hash varchar\n" + "4 my tab varchar\n" + "5 my ,tab_comma varchar\n" + "6 my, comma_tab varchar\n" + "7 my\"double_doublequote varchar\n" + "8 Abc varchar\n" + "9 varchar\n" + "10 123 varchar\n" + "Listed 11 rows\n" + "3. insert 1 row of data with values same as column names\n" + "4. insert 1 row of data with values same as column names but without enclosing double quotes\n"); sb.setLength(0); // clear the output log buffer // query each column separately int n = 0; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "5. show content of column(s): \"my space\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my space my space\n" + "Data rows:\n" + "\"my space\"\n" + "my space\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "6. show content of column(s): \"my, comma_space\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my, comma_space my, comma_space\n" + "Data rows:\n" + "\"my, comma_space\"\n" + "my, comma_space\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "7. show content of column(s): \"my$dollar\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my$dollar my$dollar\n" + "Data rows:\n" + "\"my$dollar\"\n" + "my$dollar\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "8. show content of column(s): \"my#hash\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my#hash my#hash\n" + "Data rows:\n" + "\"my#hash\"\n" + "my#hash\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "9. show content of column(s): \"my tab\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my tab my tab\n" + "Data rows:\n" + "\"my tab\"\n" + "my tab\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "10. show content of column(s): \"my ,tab_comma\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my ,tab_comma my ,tab_comma\n" + "Data rows:\n" + "\"my ,tab_comma\"\n" + "my ,tab_comma\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "11. show content of column(s): \"my, comma_tab\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my, comma_tab my, comma_tab\n" + "Data rows:\n" + "\"my, comma_tab\"\n" + "my, comma_tab\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "12. show content of column(s): \"my\"\"double_doublequote\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 my\\\"double_doublequote my\\\"double_doublequote\n" + "Data rows:\n" + "\"my\"\"double_doublequote\"\n" + "my\"\"double_doublequote\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "13. show content of column(s): \"Abc\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 Abc Abc\n" + "Data rows:\n" + "\"Abc\"\n" + "Abc\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "14. show content of column(s): \" \"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 \n" + "Data rows:\n" + "\" \"\n" + " \n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer n++; executeQueryAndShowResult_6183(stmt, dqTblName, dqColNames[n], 5 + n); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "15. show content of column(s): \"123\"\n" + "Resultset with 1 columns\n" + " Column Name, Column Label:\n" + "1 123 123\n" + "Data rows:\n" + "\"123\"\n" + "123\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer // query all columns executeQueryAndShowResult_6183(stmt, dqTblName, "*", 5 + dqColNames.length); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "16. show content of column(s): *\n" + "Resultset with 11 columns\n" + " Column Name, Column Label:\n" + "1 my space my space\n" + "2 my, comma_space my, comma_space\n" + "3 my$dollar my$dollar\n" + "4 my#hash my#hash\n" + "5 my tab my tab\n" + "6 my ,tab_comma my ,tab_comma\n" + "7 my, comma_tab my, comma_tab\n" + "8 my\\\"double_doublequote my\\\"double_doublequote\n" + "9 Abc Abc\n" + "10 \n" + "11 123 123\n" + "Data rows:\n" + "\"my space\" \"my, comma_space\" \"my$dollar\" \"my#hash\" \"my tab\" \"my ,tab_comma\" \"my, comma_tab\" \"my\"\"double_doublequote\" \"Abc\" \" \" \"123\"\n" + "my space my, comma_space my$dollar my#hash my tab my ,tab_comma my, comma_tab my\"\"double_doublequote Abc 123\n" + "Listed 2 rows\n"); sb.setLength(0); // clear the output log buffer } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } // cleanup try { sb.append("Finally drop table ").append(dqTblName).append("\n"); int ret = stmt.executeUpdate("DROP TABLE " + dqTblName); if (ret != -2) sb.append(" returned: ").append(ret).append(" (expected -2)\n"); } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("BugResultSetMetaData_Bug_6183", "Finally drop table \"my dq_table\"\n"); } private void executeQueryAndShowResult_6183(Statement st, String dqTblName, String col_list, int query_count) throws SQLException { sb.append(query_count).append(". show content of column(s): ").append(col_list).append("\n"); ResultSet rs = st.executeQuery("SELECT " + col_list + " from " + dqTblName); showResultAndClose_6183(rs); } private void showResultAndClose_6183(ResultSet rs) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int rs_col_count = rsmd.getColumnCount(); sb.append("Resultset with ").append(rs_col_count).append(" columns\n"); sb.append("\tColumn Name, Column Label:\n"); for (int col = 1; col <= rs_col_count; col++) { sb.append(col).append("\t").append(rsmd.getColumnName(col)).append("\t").append(rsmd.getColumnLabel(col)).append("\n"); } sb.append("Data rows:\n"); long row_count = 0; while (rs.next()) { row_count++; for (int col = 1; col <= rs_col_count; col++) { if (col > 1) sb.append("\t"); sb.append(rs.getString(col)); } sb.append("\n"); } rs.close(); sb.append("Listed ").append(row_count).append(" rows\n"); } private void BugSetQueryTimeout_Bug_3357() { sb.setLength(0); // clear the output log buffer int originalQueryTimeout = 0; Statement st = null; try { st = con.createStatement(); originalQueryTimeout = st.getQueryTimeout(); sb.append("original getQueryTimeout = ").append(originalQueryTimeout).append("\n"); testTimeout_3357(st, 123); testTimeout_3357(st, 123456); testTimeout_3357(st, 2147483); testTimeout_3357(st, 2147484); testTimeout_3357(st, Integer.MAX_VALUE); testTimeout_3357(st, 0); testTimeout_3357(st, 10); testTimeout_3357(st, 4); testTimeout_3357(st, -1); // to generate an SQLException as negative timeouts are invalid } catch (SQLException se) { sb.append("SQLException: setQueryTimeout(timeout_value) throws: ").append(se).append("\n"); } // restore originalQueryTimeout try { sb.append("Restore original QueryTimeout = ").append(originalQueryTimeout).append("\n"); testTimeout_3357(st, originalQueryTimeout); } catch (SQLException se) { sb.append("setQueryTimeout(timeout_value) throws: ").append(se).append("\n"); } closeStmtResSet(st, null); compareExpectedOutput("BugSetQueryTimeout_Bug_3357", "original getQueryTimeout = 0\n" + "setQueryTimeout = 123. getQueryTimeout = 123\n" + "setQueryTimeout = 123456. getQueryTimeout = 123456\n" + "setQueryTimeout = 2147483. getQueryTimeout = 2147483\n" + "setQueryTimeout = 2147484. getQueryTimeout = 2147484\n" + "setQueryTimeout = 2147483647. getQueryTimeout = 2147483647\n" + "setQueryTimeout = 0. getQueryTimeout = 0\n" + "setQueryTimeout = 10. getQueryTimeout = 10\n" + "setQueryTimeout = 4. getQueryTimeout = 4\n" + "setQueryTimeout = -1. SQLException: setQueryTimeout(timeout_value) throws: java.sql.SQLException: Illegal timeout value: -1\n" + "Restore original QueryTimeout = 0\n" + "setQueryTimeout = 0. getQueryTimeout = 0\n"); } private void testTimeout_3357(Statement st, int secs) throws SQLException { sb.append("setQueryTimeout = ").append(secs).append(". "); st.setQueryTimeout(secs); // as the call to set the timeout is delayed till a statement is executed, issue a select statment ResultSet rs = st.executeQuery("SELECT " + secs); if (rs != null) rs.close(); sb.append("getQueryTimeout = ").append(st.getQueryTimeout()).append("\n"); } private void Bug_PrepStmtManyParams_7337(int nrParams) { sb.setLength(0); // clear the output log buffer final int NR_COLUMNS = nrParams; final StringBuilder sql = new StringBuilder(100 + (NR_COLUMNS * 25)); int col; Statement stmt = null; try { stmt = con.createStatement(); sb.append("0. fetch size of new statement: ").append(stmt.getFetchSize()).append("\n"); // construct the Create Table SQL text sql.append("CREATE TABLE t7337 (ID BIGINT AUTO_INCREMENT PRIMARY KEY, "); for (col = 1; col <= NR_COLUMNS; col++) { sql.append("column").append(col).append(" VARCHAR(256),"); } sql.append("column").append(col).append(" TIMESTAMP);"); sb.append("1. create table with ").append(NR_COLUMNS+2).append(" columns, sql has length: ").append(sql.length()).append("\n"); int ret = stmt.executeUpdate(sql.toString()); sb.append("2. table created. ret = ").append(ret).append("\n"); stmt.close(); stmt = null; } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); // test: PREPARE INSERT INTO with many parameters PreparedStatement pstmt = null; try { // construct the Insert Into Table SQL text, first without any parameter makers sql.setLength(0); // clear the sql buffer sql.append("INSERT INTO t7337 \n("); for (col = 1; col <= NR_COLUMNS; col++) { sql.append("column").append(col).append(","); } sql.append("column").append(col).append(" ) VALUES \n("); int posFirstPart = sql.length(); for (col = 1; col <= NR_COLUMNS; col++) { sql.append("'someTextHere',"); } sql.append("'2022-11-11');"); sb.append("3. prepare insert statement (no params), sql has length: ").append(sql.length()).append("\n"); pstmt = con.prepareStatement(sql.toString()); if (pstmt != null) { sb.append(" fetch size after prepare 1: ").append(pstmt.getFetchSize()).append("\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append(" pmd. ").append(pmd.getParameterCount()).append(" parameters\n"); sb.append("4. execute prepared insert\n"); int inserted = pstmt.executeUpdate(); sb.append("5. first execute returned: ").append(inserted).append("\n"); // do it one more time inserted = pstmt.executeUpdate(); sb.append("5. second execute returned: ").append(inserted).append("\n"); sb.append("6. inserted data committed\n"); pstmt.close(); pstmt = null; } // construct the Insert Into Table SQL text, now with parameter makers sql.setLength(posFirstPart); // clear the sql part after the: VALUES ( for (col = 1; col <= nrParams; col++) { sql.append("?,"); } sql.append("'2022-11-16');"); sb.append("7. prepare insert statement (with params), sql has length: ").append(sql.length()).append("\n"); pstmt = con.prepareStatement(sql.toString()); if (pstmt != null) { sb.append(" fetch size after prepare 2: ").append(pstmt.getFetchSize()).append("\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append(" pmd. ").append(pmd.getParameterCount()).append(" parameters\n"); sb.append("8. bind parameters\n"); for (col = 1; col <= nrParams; col++) { pstmt.setString(col, "someMoreText"); } sb.append("9. execute prepared insert with parameters\n"); int inserted = pstmt.executeUpdate(); sb.append("10. first execute returned: ").append(inserted).append("\n"); // do it one more time inserted = pstmt.executeUpdate(); sb.append("10. second execute returned: ").append(inserted).append("\n"); sb.append("11. inserted data committed\n"); pstmt.close(); pstmt = null; } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, null); compareExpectedOutput("Bug_PrepStmtManyParams_7337(" + nrParams + ")", "0. fetch size of new statement: 250\n" + "1. create table with " + (NR_COLUMNS+2) + " columns, sql has length: " + ((NR_COLUMNS * 23) -29) + "\n" + "2. table created. ret = -2\n" + "3. prepare insert statement (no params), sql has length: " + ((NR_COLUMNS * 25) -53) + "\n" + " fetch size after prepare 1: 250\n" + " pmd. 0 parameters\n" + "4. execute prepared insert\n" + "5. first execute returned: 1\n" + "5. second execute returned: 1\n" + "6. inserted data committed\n" + "7. prepare insert statement (with params), sql has length: " + ((nrParams * 12) -53) + "\n" + " fetch size after prepare 2: 250\n" + " pmd. " + nrParams + " parameters\n" + "8. bind parameters\n" + "9. execute prepared insert with parameters\n" + "10. first execute returned: 1\n" + "10. second execute returned: 1\n" + "11. inserted data committed\n"); // test also: PREPARE SELECT * FROM .. without and with many parameters sb.setLength(0); // clear the output log buffer ResultSet rs = null; try { // construct the Select SQL text, first without any parameter makers sql.setLength(0); // clear the sql buffer sql.append("SELECT * FROM t7337"); sb.append("12. prepare select statement (no params), sql has length: ").append(sql.length()).append("\n"); pstmt = con.prepareStatement(sql.toString()); if (pstmt != null) { sb.append(" fetch size after prepare 3: ").append(pstmt.getFetchSize()).append("\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append(" pmd. ").append(pmd.getParameterCount()).append(" parameters\n"); ResultSetMetaData rsmd = pstmt.getMetaData(); sb.append(" rsmd. ").append(rsmd.getColumnCount()).append(" result columns\n"); sb.append("13. execute prepared select\n"); rs = pstmt.executeQuery(); if (rs != null) { rsmd = rs.getMetaData(); sb.append("14. first query execute succeeded. it has ").append(rsmd.getColumnCount()).append(" result columns\n"); rs.close(); rs = null; } else { sb.append("14. first query execute failed to return a result\n"); } // do it one more time rs = pstmt.executeQuery(); if (rs != null) { rsmd = rs.getMetaData(); sb.append("15. second query execute succeeded. it has ").append(rsmd.getColumnCount()).append(" result columns\n"); rs.close(); rs = null; } else { sb.append("15. second query execute failed to return a result\n"); } pstmt.close(); pstmt = null; } // add the WHERE part with many parameter makers sql.append(" WHERE "); for (col = 1; col <= NR_COLUMNS; col++) { sql.append("column").append(col).append(" = ? AND "); } sql.append("column").append(col).append(" = '2022-11-16'"); sb.append("16. prepare select statement (with params), sql has length: ").append(sql.length()).append("\n"); pstmt = con.prepareStatement(sql.toString()); if (pstmt != null) { sb.append(" fetch size after prepare 4: ").append(pstmt.getFetchSize()).append("\n"); ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append(" pmd. ").append(pmd.getParameterCount()).append(" parameters\n"); ResultSetMetaData rsmd = pstmt.getMetaData(); sb.append(" rsmd. ").append(rsmd.getColumnCount()).append(" result columns\n"); sb.append("17. bind parameters\n"); for (col = 1; col <= nrParams; col++) { pstmt.setString(col, "someMoreText"); } sb.append("18. execute prepared select\n"); rs = pstmt.executeQuery(); if (rs != null) { rsmd = rs.getMetaData(); sb.append("19. first query execute succeeded. it has ") .append(rsmd.getColumnCount()).append(" result columns and "); int rows = 0; while (rs.next()) rows++; sb.append(rows).append(" rows\n"); rs.close(); rs = null; } else { sb.append("19. first query execute failed to return a result\n"); } // do it one more time sb.append("20. bind parameters\n"); for (col = 1; col <= nrParams; col++) { pstmt.setString(col, "someMoreText"); } sb.append("21. execute prepared select again\n"); rs = pstmt.executeQuery(); if (rs != null) { rsmd = rs.getMetaData(); sb.append("22. second query execute succeeded. it has ") .append(rsmd.getColumnCount()).append(" result columns and "); int rows = 0; while (rs.next()) rows++; sb.append(rows).append(" rows\n"); rs.close(); rs = null; } else { sb.append("22. second query execute failed to return a result\n"); } pstmt.close(); pstmt = null; } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(pstmt, rs); // cleanup table try { stmt = con.createStatement(); stmt.executeUpdate("DROP TABLE IF EXISTS t7337;"); stmt.close(); stmt = null; } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, null); compareExpectedOutput("Bug_PrepStmtManyParams_7337(" + nrParams + ")", "12. prepare select statement (no params), sql has length: 19\n" + " fetch size after prepare 3: 250\n" + " pmd. 0 parameters\n" + " rsmd. " + (NR_COLUMNS+2) + " result columns\n" + "13. execute prepared select\n" + "14. first query execute succeeded. it has " + (NR_COLUMNS+2) + " result columns\n" + "15. second query execute succeeded. it has " + (NR_COLUMNS+2) + " result columns\n" + "16. prepare select statement (with params), sql has length: " + ((NR_COLUMNS * 18) -58) + "\n" + " fetch size after prepare 4: 250\n" + " pmd. " + nrParams + " parameters\n" + " rsmd. " + (NR_COLUMNS+2) + " result columns\n" + "17. bind parameters\n" + "18. execute prepared select\n" + "19. first query execute succeeded. it has " + (NR_COLUMNS+2) + " result columns and 2 rows\n" + "20. bind parameters\n" + "21. execute prepared select again\n" + "22. second query execute succeeded. it has " + (NR_COLUMNS+2) + " result columns and 2 rows\n"); } /** * This SQLcopyinto program demonstrates how the MonetDB JDBC driver can facilitate * in performing COPY INTO ... FROM STDIN sequences. * It shows how a data stream via MapiSocket to STDIN can be performed. * * @author Fabian Groffen, Martin van Dinther */ private void SQLcopyinto(final String conn_URL) { sb.setLength(0); // clear the output log buffer final String tablenm = "exampleSQLCopyInto"; Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); stmt.execute("CREATE TABLE IF NOT EXISTS " + tablenm + " (id int, val varchar(24))"); fillTableUsingCopyIntoSTDIN(conn_URL, tablenm); // check content of the table populated via COPY INTO ... FROM STDIN sb.append("Listing uploaded data:\n"); int row = 0; rs = stmt.executeQuery("SELECT * FROM " + tablenm); if (rs != null) { while (rs.next()) { row++; if ((row % 1000) == 0) sb.append("Row data: ").append(rs.getString(1)).append(", ").append(rs.getString(2)).append("\n"); } rs.close(); rs = null; } } catch (SQLException se) { sb.append("SQLException: ").append(se.getMessage()).append("\n"); } catch (Exception e) { sb.append("Exception: ").append(e.getMessage()).append("\n"); } // cleanup try { stmt.execute("DROP TABLE " + tablenm); sb.append("SQLcopyinto completed\n"); } catch (SQLException se) { sb.append("SQLException: ").append(se.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); compareExpectedOutput("SQLcopyinto()", "CopyInto STDIN begin\n" + "Before connecting to MonetDB server via MapiSocket\n" + "Connected to MonetDB server via MapiSocket\n" + "Before sending data to STDIN\n" + "Completed sending data via STDIN\n" + "CopyInto STDIN end\n" + "Listing uploaded data:\n" + "Row data: 999, val_999\n" + "Row data: 1999, val_1999\n" + "Row data: 2999, val_2999\n" + "Row data: 3999, val_3999\n" + "Row data: 4999, val_4999\n" + "Row data: 5999, val_5999\n" + "Row data: 6999, val_6999\n" + "Row data: 7999, val_7999\n" + "Row data: 8999, val_8999\n" + "SQLcopyinto completed\n"); } private void fillTableUsingCopyIntoSTDIN(final String conn_URL, final String tablenm) throws Exception { sb.append("CopyInto STDIN begin\n"); org.monetdb.mcl.net.MapiSocket server = new org.monetdb.mcl.net.MapiSocket(); try { MonetConnection mcon = (MonetConnection) con; Properties props = mcon.getConnectionProperties(); sb.append("Before connecting to MonetDB server via MapiSocket\n"); List<String> warning = server.connect("jdbc:monetdb:", props); if (warning != null) { for (Iterator<String> it = warning.iterator(); it.hasNext(); ) { sb.append("Warning: ").append(it.next().toString()).append("\n"); } } sb.append("Connected to MonetDB server via MapiSocket\n"); org.monetdb.mcl.io.BufferedMCLReader mclIn = server.getReader(); org.monetdb.mcl.io.BufferedMCLWriter mclOut = server.getWriter(); String error = mclIn.discardRemainder(); if (error != null) sb.append("Received start error: ").append(error).append("\n"); sb.append("Before sending data to STDIN\n"); // the leading 's' is essential, since it is a protocol marker // that should not be omitted, likewise the trailing semicolon mclOut.write('s'); mclOut.write("COPY INTO " + tablenm + " FROM STDIN USING DELIMITERS ',',E'\\n';"); mclOut.newLine(); // now write the row data values as csv data lines to the STDIN stream for (int i = 0; i < 9000; i++) { mclOut.write("" + i + ",val_" + i); mclOut.newLine(); } mclOut.writeLine(""); // need this one for synchronisation over flush() error = mclIn.discardRemainder(); if (error != null) sb.append("Received error: ").append(error).append("\n"); mclOut.writeLine(""); // need this one for synchronisation over flush() error = mclIn.discardRemainder(); if (error != null) sb.append("Received finish error: ").append(error).append("\n"); sb.append("Completed sending data via STDIN\n"); } catch (Exception e) { sb.append("Mapi Exception: ").append(e.getMessage()).append("\n"); } finally { // close MAPI connection to MonetDB server server.close(); } sb.append("CopyInto STDIN end\n"); } private void DecimalPrecisionAndScale() { sb.setLength(0); // clear the output log buffer Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); rs = stmt.executeQuery( "select" + " cast(123456789 as DECIMAL(18,0)) as dec1800" + ", cast(123456789.0 as DECIMAL(18,1)) as dec1801" + ", cast(123456789.0 as DECIMAL(18,2)) as dec1802" + ", cast(123456789.0 as DECIMAL(18,3)) as dec1803" + ", cast(123456789.0 as DECIMAL(18,4)) as dec1804" + ", cast(123456789.0 as DECIMAL(18,5)) as dec1805" + ", cast(123456789.0 as DECIMAL(18,6)) as dec1806" + ", cast(123456789.0 as DECIMAL(18,7)) as dec1807" + ", cast(123456789.0 as DECIMAL(18,8)) as dec1808" + ", cast(123456789.0 as DECIMAL(18,9)) as dec1809" + ", cast(12345678.9 as DECIMAL(18,10)) as dec1810" + ", cast(1234567.89 as DECIMAL(18,11)) as dec1811" + ", cast(123456.789 as DECIMAL(18,12)) as dec1812;"); if (rs != null) { ResultSetMetaData rsmd = rs.getMetaData(); final int rscolcnt = rsmd.getColumnCount(); sb.append("Query has ").append(rscolcnt).append(" columns:\n"); sb.append("colnr\tlabel\ttypenm\tdisplaylength\tprecision\tscale\n"); for (int col = 1; col <= rscolcnt; col++) { sb.append("col ").append(col); sb.append("\t").append(rsmd.getColumnLabel(col)); sb.append("\t").append(rsmd.getColumnTypeName(col)); sb.append("\t").append(rsmd.getColumnDisplaySize(col)); sb.append("\t").append(rsmd.getPrecision(col)); sb.append("\t").append(rsmd.getScale(col)); sb.append("\n"); } sb.append("Values\n"); while (rs.next()) { sb.append("colnr\tasString\tasBigDecimal\n"); for (int col = 1; col <= rscolcnt; col++) { sb.append("col ").append(col); sb.append("\t").append(rs.getString(col)); sb.append("\t").append(rs.getBigDecimal(col)); sb.append("\n"); } sb.append("\n"); } rs.close(); rs = null; } } catch (SQLException se) { sb.append("SQLException: ").append(se.getMessage()).append("\n"); } catch (Exception e) { sb.append("Exception: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); // The precision should be 18 and the scale should be from 0 to 12. compareExpectedOutput("DecimalPrecisionAndScale()", "Query has 13 columns:\n" + "colnr label typenm displaylength precision scale\n" + "col 1 dec1800 decimal 19 18 0\n" + "col 2 dec1801 decimal 20 18 1\n" + "col 3 dec1802 decimal 20 18 2\n" + "col 4 dec1803 decimal 20 18 3\n" + "col 5 dec1804 decimal 20 18 4\n" + "col 6 dec1805 decimal 20 18 5\n" + "col 7 dec1806 decimal 20 18 6\n" + "col 8 dec1807 decimal 20 18 7\n" + "col 9 dec1808 decimal 20 18 8\n" + "col 10 dec1809 decimal 20 18 9\n" + "col 11 dec1810 decimal 20 18 10\n" + "col 12 dec1811 decimal 20 18 11\n" + "col 13 dec1812 decimal 20 18 12\n" + "Values\n" + "colnr asString asBigDecimal\n" + "col 1 123456789 123456789\n" + "col 2 123456789.0 123456789.0\n" + "col 3 123456789.00 123456789.00\n" + "col 4 123456789.000 123456789.000\n" + "col 5 123456789.0000 123456789.0000\n" + "col 6 123456789.00000 123456789.00000\n" + "col 7 123456789.000000 123456789.000000\n" + "col 8 123456789.0000000 123456789.0000000\n" + "col 9 123456789.00000000 123456789.00000000\n" + "col 10 123456789.000000000 123456789.000000000\n" + "col 11 12345678.9000000000 12345678.9000000000\n" + "col 12 1234567.89000000000 1234567.89000000000\n" + "col 13 123456.789000000000 123456.789000000000\n" + "\n"); } private void Test_ClientInfo(String con_URL) { if (!isPostDec2023) return; sb.setLength(0); final String[] known = { "ApplicationName", "ClientHostname", "ClientLibrary", "ClientPid", "ClientRemark" }; try { sb.append("Connecting\n"); try (Connection conn = DriverManager.getConnection(con_URL)) { // Server metadata includes list of supported clientinfo properties sb.append("Fetching supported clientinfo properties\n"); DatabaseMetaData md = conn.getMetaData(); try (ResultSet rs = md.getClientInfoProperties()) { HashSet<String> seen = new HashSet<>(); while (rs.next()) { String name = rs.getString(1); if (name == null || name.isEmpty()) { sb.append("NAME column contains empty string\n"); } seen.add(name); int width = rs.getInt(2); if (width <= 0) { sb.append("MAX_LEN for " + name + " is " + width + "\n"); } String description = rs.getString(4); if (description == null || description.isEmpty()) { sb.append("DESCRIPTION for " + name + " is empty\n"); } } for (String name: known) { boolean found = seen.contains(name); sb.append("- " + name + (found ? " was " : " was not ") + "found\n"); } } // I cannot think of a way to check the default values that doesn't // essentially duplicate the code that came up with the default values. // The best we can do is verify they're not empty. sb.append("Check initial values.\n"); Properties initialValues = conn.getClientInfo(); for (String name: known) { String value = (String) initialValues.getOrDefault(name, ""); sb.append("- " + name + (value.isEmpty() ? " is empty" : " is not empty") + "\n"); } // We should get a fresh copy every time if (conn.getClientInfo() != initialValues) sb.append("Calls to con.getClientInfo do not return references to the same Properties object\n"); else sb.append("Calls to con.getClientInfo DO return references to the same Properties object!\n"); // Assign new values in various ways. // Also include some unknown properties sb.append("Set ApplicationName=1\n"); conn.setClientInfo("ApplicationName", "1"); readWarnings(conn.getWarnings()); conn.clearWarnings(); sb.append("Set BananaNameXYZ=99\n"); conn.setClientInfo("BananaNameXYZ", "99"); readWarnings(conn.getWarnings()); conn.clearWarnings(); sb.append("Set { ClientHostname=2, ClientLibrary=3, ClientPid=4, ClientRemark=5, ClientBananaPQR=999 }\n"); Properties toBeInserted = new Properties(); toBeInserted.put("ClientHostname", "2"); toBeInserted.put("ClientLibrary", "3"); toBeInserted.put("ClientPid", "4"); toBeInserted.put("ClientRemark", "5"); toBeInserted.put("ClientBananaPQR", "999"); conn.setClientInfo(toBeInserted); readWarnings(conn.getWarnings()); conn.clearWarnings(); sb.append("Checking the results\n"); Properties foundValues = conn.getClientInfo(); for (String name: known) { sb.append("- " + name + ": prop=" ); String propValue = (String) foundValues.getOrDefault(name, ""); sb.append("" + propValue); sb.append(", single="); String singleValue = conn.getClientInfo(name); sb.append("" + singleValue); if (propValue != null && !propValue.equals(singleValue)) sb.append(" !!! DIFFERENT !!!"); sb.append("\n"); } } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); } compareExpectedOutput("Test_ClientInfo", "Connecting\n" + "Fetching supported clientinfo properties\n" + "- ApplicationName was found\n" + "- ClientHostname was found\n" + "- ClientLibrary was found\n" + "- ClientPid was found\n" + "- ClientRemark was found\n" + "Check initial values.\n" + "- ApplicationName is not empty\n" + "- ClientHostname is not empty\n" + "- ClientLibrary is not empty\n" + "- ClientPid is not empty\n" + "- ClientRemark is empty\n" + "Calls to con.getClientInfo do not return references to the same Properties object\n" + "Set ApplicationName=1\n" + "Set BananaNameXYZ=99\n" + "Warning: java.sql.SQLWarning: unknown client info property: BananaNameXYZ\n" + "Set { ClientHostname=2, ClientLibrary=3, ClientPid=4, ClientRemark=5, ClientBananaPQR=999 }\n" + "Warning: java.sql.SQLWarning: unknown client info property: ClientBananaPQR\n" + "Checking the results\n" + "- ApplicationName: prop=1, single=1\n" + "- ClientHostname: prop=2, single=2\n" + "- ClientLibrary: prop=3, single=3\n" + "- ClientPid: prop=4, single=4\n" + "- ClientRemark: prop=5, single=5\n" ); } // some private utility methods for showing table content and params meta data private void showTblContents(String tblnm) { final String query = "SELECT * FROM \"" + tblnm + "\""; Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); rs = stmt.executeQuery(query); if (rs != null) { ResultSetMetaData rsmd = rs.getMetaData(); sb.append("Table ").append(tblnm).append(" has ").append(rsmd.getColumnCount()).append(" columns:\n"); for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append("\t").append(rsmd.getColumnLabel(col)); } sb.append("\n"); while (rs.next()) { for (int col = 1; col <= rsmd.getColumnCount(); col++) { sb.append("\t").append(rs.getString(col)); } sb.append("\n"); } } else sb.append("failed to execute query: ").append(query).append("\n"); } catch (SQLException e) { sb.append("showContents failed: ").append(e.getMessage()).append("\n"); } closeStmtResSet(stmt, rs); } private void showParams(PreparedStatement pstmt) { try { // testing and showing parameter meta data ParameterMetaData pmd = pstmt.getParameterMetaData(); sb.append("pmd. ").append(pmd.getParameterCount()).append(" parameters:\n"); for (int parm = 1; parm <= pmd.getParameterCount(); parm++) { sb.append("Param ").append(parm).append("\n"); int nullable = pmd.isNullable(parm); sb.append(" nullable ").append(nullable).append(" ("); switch (nullable) { case ParameterMetaData.parameterNoNulls: sb.append("NO"); break; case ParameterMetaData.parameterNullable: sb.append("YA"); break; case ParameterMetaData.parameterNullableUnknown: sb.append("UNKNOWN"); break; default: sb.append("INVALID ").append(nullable); break; } sb.append(")\n"); sb.append(" signed ").append(pmd.isSigned(parm)).append("\n"); sb.append(" precision ").append(pmd.getPrecision(parm)).append("\n"); sb.append(" scale ").append(pmd.getScale(parm)).append("\n"); sb.append(" type ").append(pmd.getParameterType(parm)).append("\n"); sb.append(" typename ").append(pmd.getParameterTypeName(parm)).append("\n"); sb.append(" classname ").append(pmd.getParameterClassName(parm)).append("\n"); int mode = pmd.getParameterMode(parm); sb.append(" mode ").append(mode).append(" ("); switch (mode) { case ParameterMetaData.parameterModeIn: sb.append("IN"); break; case ParameterMetaData.parameterModeInOut: sb.append("INOUT"); break; case ParameterMetaData.parameterModeOut: sb.append("OUT"); break; case ParameterMetaData.parameterModeUnknown: sb.append("UNKNOWN"); break; default: sb.append("INVALID ").append(mode); break; } sb.append(")\n"); } } catch (SQLException e) { sb.append("showParams() FAILED: ").append(e.getMessage()).append("\n"); } } private void readExceptions(SQLException e) { while (e != null) { sb.append("Exception: ").append(e.toString()).append("\n"); e = e.getNextException(); } } private void readWarnings(SQLWarning w) { while (w != null) { sb.append("Warning: ").append(w.toString()).append("\n"); w = w.getNextWarning(); } } private void compareExpectedOutput(String testname, String expected) { final String produced = sb.toString(); if (!expected.equals(produced)) { foundDifferences = true; System.err.print("Test '"); System.err.print(testname); if (!testname.endsWith(")") && !testname.endsWith(";")) System.err.print("()"); System.err.println("' produced different output!"); int expLen = expected.length(); int prodLen = produced.length(); if (expLen > 0 && prodLen > 0) { int max_pos = expLen; if (prodLen > max_pos) max_pos = prodLen; int line = 1; int rowpos = 0; for (int pos = 0; pos < max_pos; pos++) { char a = (pos < expLen ? expected.charAt(pos) : '~'); char b = (pos < prodLen ? produced.charAt(pos) : '~'); if (a == '\n') { line++; rowpos = 0; } else { rowpos++; } if (a != b) { if (pos + 40 < expLen) expLen = pos + 40; if (pos + 40 < prodLen) prodLen = pos + 40; System.err.println("Difference found at line " + line + " position " + rowpos + ". Expected:\n\"" + expected.substring(pos < expLen ? pos : expLen-1, expLen-1) + "\"\nFound:\n\"" + produced.substring(pos < prodLen ? pos : prodLen-1, prodLen-1) + "\""); pos = max_pos; } } } System.err.println(); System.err.println("---- Full Output: ------------"); System.err.println(sb); System.err.println("---- END ---------------------"); System.err.println("---- Expected Output: --------"); System.err.println(expected); System.err.println("---- END ---------------------"); System.err.println(); } if (sb.length() > sbInitLen) { System.err.println("Test '" + testname + "' produced output > " + sbInitLen + " chars! Enlarge sbInitLen to: " + sb.length()); } } private void closeConx(Connection cn) { if (cn != null) { try { cn.close(); } catch (SQLException e) { /* ignore */ } } } private void closeStmtResSet(Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { /* ignore */ } } if (st != null) { try { st.close(); } catch (SQLException e) { /* ignore */ } } } }