/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.sql.Timestamp; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.regex.Pattern; import java.io.PrintStream; /** * Some fairly crude tests. All tests are confided to the schema * "javatest" * * @author Thomas Hallgren */ public class Tester { private static final int CMD_AMBIGUOUS = -2; private static final int CMD_UNKNOWN = -1; private static final int CMD_USER = 0; private static final int CMD_PASSWORD = 1; private static final int CMD_DATABASE = 2; private static final int CMD_HOSTNAME = 3; private static final int CMD_PORT = 4; private static final int CMD_DEBUG = 5; private static final int CMD_EXCLUDE = 6; private final Connection m_connection; private static final ArrayList s_commands = new ArrayList(); static { s_commands.add(CMD_USER, "user"); s_commands.add(CMD_PASSWORD, "password"); s_commands.add(CMD_DATABASE, "database"); s_commands.add(CMD_HOSTNAME, "host"); s_commands.add(CMD_PORT, "port"); s_commands.add(CMD_DEBUG, "debug"); s_commands.add(CMD_EXCLUDE, "exclude"); } private static final int getCommand(String arg) { int top = s_commands.size(); int candidateCmd = CMD_UNKNOWN; for(int idx = 0; idx < top; ++idx) { if(((String)s_commands.get(idx)).startsWith(arg)) { if(candidateCmd != CMD_UNKNOWN) return CMD_AMBIGUOUS; candidateCmd = idx; } } return candidateCmd; } public static void printUsage() { PrintStream out = System.err; out.println("usage: java org.postgresql.pljava.test.Tester"); out.println(" [ -host <hostName> ] # default is localhost"); out.println(" [ -port <portNubmer> ] # default is blank"); out .println(" [ -database <database> ] # default is name of current user"); out .println(" [ -user <userName> ] # default is name of current user"); out.println(" [ -password <password> ] # default is no password"); out .println(" [ -exclude excludePattern ] # exclude tests matching pattern"); out .println(" [ -debug ] # wait for debugger to attach to backend"); } public static void main(String[] argv) { String driverClass = "org.postgresql.Driver"; String hostName = "localhost"; String portNumber = null; String userName = System.getProperty("user.name", "postgres"); String database = userName; String subsystem = "postgresql"; String password = null; String exclude = null; boolean debug = false; int top = argv.length; for(int idx = 0; idx < top; ++idx) { String arg = argv[idx]; if(arg.length() < 2) { printUsage(); return; } if(arg.charAt(0) == '-') { int optCmd = getCommand(arg.substring(1)); switch(optCmd) { case CMD_DEBUG: debug = true; break; case CMD_USER: if(++idx < top) { userName = argv[idx]; if(userName.length() > 0 && userName.charAt(0) != '-') break; } printUsage(); return; case CMD_PASSWORD: if(++idx < top) { password = argv[idx]; if(password.length() > 0 && password.charAt(0) != '-') break; } printUsage(); return; case CMD_DATABASE: if(++idx < top) { database = argv[idx]; if(database.length() > 0 && database.charAt(0) != '-') break; } printUsage(); return; case CMD_HOSTNAME: if(++idx < top) { hostName = argv[idx]; if(hostName.length() > 0 && hostName.charAt(0) != '-') break; } printUsage(); return; case CMD_EXCLUDE: if(++idx < top) { exclude = argv[idx]; if(exclude.length() > 0 && exclude.charAt(0) != '-') break; } printUsage(); return; case CMD_PORT: if(++idx < top) { portNumber = argv[idx]; if(portNumber.length() > 0 && portNumber.charAt(0) != '-') break; } printUsage(); return; default: printUsage(); return; } } } try { Class.forName(driverClass); StringBuffer cc = new StringBuffer(); cc.append("jdbc:"); cc.append(subsystem); cc.append("://"); cc.append(hostName); if(portNumber != null) { cc.append(':'); cc.append(portNumber); } cc.append('/'); cc.append(database); Connection c = DriverManager.getConnection(cc.toString(), userName, password); Tester t = new Tester(c); if(debug) { System.out.println("Attach debugger to backend"); Thread.sleep(30000); System.out.println("continuing"); } for(int idx = 0; idx < 10; ++idx) { Pattern p = (exclude == null) ? null : Pattern.compile(exclude, Pattern.CASE_INSENSITIVE); if(p == null || !p.matcher("parameters").matches()) t.testParameters(); if(p == null || !p.matcher("usernametrigger").matches()) t.testInsertUsernameTrigger(); if(p == null || !p.matcher("moddatetimetrigger").matches()) t.testModdatetimeTrigger(); if(p == null || !p.matcher("spiactions").matches()) t.testSPIActions(); if(p == null || !p.matcher("tuplereturn").matches()) t.testTupleReturn(); if(p == null || !p.matcher("setreturn").matches()) t.testSetReturn(); if(p == null || !p.matcher("callincall").matches()) t.testCallInCall(); if(p == null || !p.matcher("currentdir").matches()) t.testCurrentDir(); if(p == null || !p.matcher("usingproperties").matches()) t.testUsingProperties(); if(p == null || !p.matcher("usingscalarproperties").matches()) t.testUsingScalarProperties(); if(p == null || !p.matcher("usingresultsetproperties").matches()) t.testUsingResultSetProperties(); if(p == null || !p.matcher("savepointsanity").matches()) t.testSavepointSanity(); if(p == null || !p.matcher("transactionrecovery").matches()) t.testTransactionRecovery(); if(p == null || !p.matcher("trustedsecurity").matches()) t.testTrustedSecurity(); if(p == null || !p.matcher("binarycolumns").matches()) t.testBinaryColumns(); if(p == null || !p.matcher("databasemetadata").matches()) t.testDatabaseMetaData(); if(p == null || !p.matcher("resultset").matches()) t.testResultSet(); if(p == null || !p.matcher("complexscalar").matches()) t.testComplexScalar(); if(p == null || !p.matcher("complextuple").matches()) t.testComplexTuple(); } t.close(); } catch(Exception e) { e.printStackTrace(); } } public Tester(Connection conn) throws SQLException { Statement stmt = conn.createStatement(); stmt.execute("SET search_path TO javatest,public"); stmt.close(); m_connection = conn; } public void close() throws SQLException { m_connection.close(); } public void testParameters() throws SQLException { this.testTimestamp(); this.testInt(); } public void testSPIActions() throws SQLException { System.out.println("*** testSPIActions()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM employees1"); stmt.execute("DELETE FROM employees2"); stmt.execute("INSERT INTO employees1 VALUES(" + "1, 'Calvin Forrester', 10000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "2, 'Edwin Archer', 20000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "3, 'Rebecka Shawn', 30000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "4, 'Priscilla Johnson', 25000)"); stmt.execute("SELECT transferPeople(20000)"); ResultSet rs = stmt.executeQuery("SELECT * FROM employees2"); while(rs.next()) { int id = rs.getInt(1); String name = rs.getString(2); int salary = rs.getInt(3); System.out.println("Id = \"" + id + "\", name = \"" + name + "\", salary = \"" + salary + "\""); } rs.close(); } public void testTupleReturn() throws SQLException { System.out.println("*** testTupleReturn()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT tupleReturnToString(tupleReturnExample(1, 5))"); while(rs.next()) { String str = rs.getString(1); System.out.println(str); } rs.close(); rs = stmt.executeQuery("SELECT tupleReturnToString(tupleReturnExample2(1, NULL))"); while(rs.next()) { String str = rs.getString(1); System.out.println(str); } rs.close(); } public void testSetReturn() throws SQLException { System.out.println("*** testSetReturn()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT base, incbase, ctime FROM setReturnExample(1, 5)"); while(rs.next()) { int base = rs.getInt(1); int incbase = rs.getInt(2); Timestamp ctime = rs.getTimestamp(3); System.out.println("Base = \"" + base + "\", incbase = \"" + incbase + "\", ctime = \"" + ctime + "\""); } rs.close(); } public void testUsingProperties() throws SQLException { System.out.println("*** testUsingProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT name, value FROM propertyExample()"); while(rs.next()) { String name = rs.getString(1); String value = rs.getString(2); System.out.println("Name = \"" + name + "\", value = \"" + value + "\""); } rs.close(); } public void testUsingResultSetProperties() throws SQLException { System.out.println("*** testUsingResultSetProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT name, value FROM resultSetPropertyExample()"); while(rs.next()) { String name = rs.getString(1); String value = rs.getString(2); System.out.println("Name = \"" + name + "\", value = \"" + value + "\""); } rs.close(); } public void testUsingScalarProperties() throws SQLException { System.out.println("*** testUsingScalarProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT scalarPropertyExample()"); while(rs.next()) System.out.println(rs.getString(1)); rs.close(); } public void testBinaryColumns() throws SQLException { System.out.println("*** testBinaryColumns()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM binaryColumnTest()"); while(rs.next()) { byte[] b1 = rs.getBytes(1); byte[] b2 = rs.getBytes(2); if(!Arrays.equals(b1, b2)) throw new SQLException("binary columns differ"); } rs.close(); } public void testCallInCall() throws SQLException { System.out.println("*** testCallInCall()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT maxFromSetReturnExample(10, 8)"); while(rs.next()) { int max = rs.getInt(1); System.out.println("Max = \"" + max + "\""); } } public void testModdatetimeTrigger() throws SQLException { System.out.println("*** testModdatetimeTrigger()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM mdt"); stmt.execute("INSERT INTO mdt VALUES (1, 'first')"); stmt.execute("INSERT INTO mdt VALUES (2, 'second')"); stmt.execute("INSERT INTO mdt VALUES (3, 'third')"); ResultSet rs = stmt.executeQuery("SELECT * FROM mdt"); while(rs.next()) { int id = rs.getInt(1); String idesc = rs.getString(2); Timestamp moddate = rs.getTimestamp(3); System.out.println("Id = \"" + id + "\", idesc = \"" + idesc + "\", moddate = \"" + moddate + "\""); } rs.close(); stmt.execute("UPDATE mdt SET id = 4 WHERE id = 1"); stmt.execute("UPDATE mdt SET id = 5 WHERE id = 2"); stmt.execute("UPDATE mdt SET id = 6 WHERE id = 3"); rs = stmt.executeQuery("SELECT * FROM mdt"); while(rs.next()) { int id = rs.getInt(1); String idesc = rs.getString(2); Timestamp moddate = rs.getTimestamp(3); System.out.println("Id = \"" + id + "\", idesc = \"" + idesc + "\", moddate = \"" + moddate + "\""); } rs.close(); stmt.close(); } public void testInsertUsernameTrigger() throws SQLException { System.out.println("*** testInsertUsernameTrigger()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM username_test"); stmt.execute("INSERT INTO username_test VALUES ('nothing', 'thomas')"); stmt.execute("INSERT INTO username_test VALUES ('null', null)"); stmt.execute("INSERT INTO username_test VALUES ('empty string', '')"); stmt.execute("INSERT INTO username_test VALUES ('space', ' ')"); stmt.execute("INSERT INTO username_test VALUES ('tab', ' ')"); stmt.execute("INSERT INTO username_test VALUES ('name', 'name')"); ResultSet rs = stmt.executeQuery("SELECT * FROM username_test"); while(rs.next()) { String name = rs.getString(1); String username = rs.getString(2); System.out.println("Name = \"" + name + "\", username = \"" + username + "\""); } rs.close(); // Test the update trigger as well as leaking statements // // System.out.println("Doing 800 updates with double triggers that leak // statements"); // System.out.println("(but not leak memory). One trigger executes SQL // that reenters PL/Java"); // for(int idx = 0; idx < 200; ++idx) // { // stmt.execute("UPDATE username_test SET username = 'Kalle Kula' WHERE // username = 'name'"); // stmt.execute("UPDATE username_test SET username = 'Pelle Kanin' WHERE // username = 'thomas'"); // stmt.execute("UPDATE username_test SET username = 'thomas' WHERE // username = 'Kalle Kula'"); // stmt.execute("UPDATE username_test SET username = 'name' WHERE // username = 'Pelle Kanin'"); // } stmt.close(); } public void testTimestamp() throws SQLException { System.out.println("*** testTimestamp()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_getTimestamp(), java_getTimestamptz()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Timestamp = " + rs.getTimestamp(1) + ", Timestamptz = " + rs.getTimestamp(2)); rs.close(); // Test parameter overloading. Set log_min_messages (in posgresql.conf) // to INFO or higher and watch the result. // stmt.execute("SELECT print(current_date)"); stmt.execute("SELECT print(current_time)"); stmt.execute("SELECT print(current_timestamp)"); stmt.close(); } public void testInt() throws SQLException { System.out.println("*** testInt()"); /* * Test parameter override from int primitive to java.lang.Integer Test * return value override (stipulated by the Java method rather than in * the function declaration. Seems to be that way according to the * SQL-2003 spec). Test function call within function call and function * that returns an object rather than a primitive to reflect null * values. */ Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_addOne(java_addOne(54)), nullOnEven(1), nullOnEven(2)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else { System.out.println("54 + 2 = " + rs.getInt(1)); int n = rs.getInt(2); System.out.println("nullOnEven(1) = " + (rs.wasNull() ? "null" : Integer.toString(n))); n = rs.getInt(3); System.out.println("nullOnEven(2) = " + (rs.wasNull() ? "null" : Integer.toString(n))); } rs.close(); stmt.close(); } public void testComplexScalar() throws SQLException { System.out.println("*** testComplexScalar()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT logcomplex('(34.56,12.78)'::complex)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println(rs.getString(1)); rs.close(); stmt.close(); } public void testComplexTuple() throws SQLException { System.out.println("*** testComplexTuple()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT logcomplex((34.56,12.78)::complextuple)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println(rs.getString(1)); rs.close(); stmt.close(); } public void testCurrentDir() throws SQLException { System.out.println("*** testCurrentDir()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_getSystemProperty('user.dir')"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Server directory = " + rs.getString(1)); rs.close(); stmt.close(); } public void testSavepointSanity() throws SQLException { System.out.println("*** testSavepointSanity()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT testSavepointSanity()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Savepoint sanity = " + rs.getInt(1)); rs.close(); stmt.close(); } public void testTransactionRecovery() throws SQLException { System.out.println("*** testTransactionRecovery()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT testTransactionRecovery()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Transaction recovery = " + rs.getInt(1)); rs.close(); stmt.close(); } public void testTrustedSecurity() throws SQLException { System.out.println("*** testTrustedSecurity()"); Statement stmt = m_connection.createStatement(); ResultSet rs = null; tryCreateTempFile(true); boolean funcCreated = false; try { rs = stmt.executeQuery("SHOW IS_SUPERUSER"); if(!(rs.next() && rs.getString(1).equals("on"))) { System.out .println("Tester is not superuser so tests on untrusted language cannot be performed"); return; } // Try the same java method again, this time using language javaU // System.out.println("*** testUntrustedSecurity()"); stmt .execute("CREATE OR REPLACE FUNCTION javatest.create_temp_file_untrusted()" + " RETURNS varchar" + " AS 'org.postgresql.pljava.example.Security.createTempFile'" + " LANGUAGE javaU"); tryCreateTempFile(false); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } if(funcCreated) stmt .execute("DROP FUNCTION javatest.create_temp_file_untrusted()"); stmt.close(); } } private void tryCreateTempFile(boolean trusted) throws SQLException { Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { if(trusted) rs = stmt.executeQuery("SELECT create_temp_file_trusted()"); else rs = stmt.executeQuery("SELECT create_temp_file_untrusted()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Name of created temp file = " + rs.getString(1)); if(trusted) throw new RuntimeException( "ERROR: Tempfile creation succeded although language is trusted!"); } catch(SQLException e) { if(!trusted) throw e; System.out .println("OK, creation of temp file was *unsuccessful* as it should be"); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } stmt.close(); } } public void testDatabaseMetaData() throws SQLException { Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { System.out.println("*** DatabaseMetaData 'String' functions:"); rs = stmt .executeQuery("SELECT * FROM javatest.getMetaDataStrings()"); while(rs.next()) { String methodName = rs.getString(1); String methodResult = rs.getString(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); System.out.println("*** DatabaseMetaData 'boolean' functions:"); rs = stmt .executeQuery("SELECT * FROM javatest.getMetaDataBooleans()"); while(rs.next()) { String methodName = rs.getString(1); boolean methodResult = rs.getBoolean(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); System.out.println("*** DatabaseMetaData 'int' functions:"); rs = stmt.executeQuery("SELECT * FROM javatest.getMetaDataInts()"); while(rs.next()) { String methodName = rs.getString(1); int methodResult = rs.getInt(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); executeMetaDataFunction(stmt, "getAttributes((String)null,\"javatest\",\"%\",\"%\")"); executeMetaDataFunction(stmt, "getBestRowIdentifier((String)null,\"sqlj\",\"jar_repository\",0,FALSE)"); executeMetaDataFunction(stmt, "getCatalogs()"); executeMetaDataFunction(stmt, "getColumnPrivileges((String)null,\"sqlj\",\"jar_repository\",\"jarid\")"); executeMetaDataFunction(stmt, "getColumns((String)null,\"sqlj\",\"jar_repository\",\"%\")"); executeMetaDataFunction( stmt, "getCrossReference((String)null,\"sqlj\",\"jar_repository\",(String)null,\"sqlj\",\"jar_entry\")"); executeMetaDataFunction(stmt, "getExportedKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getImportedKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getIndexInfo((String)null,\"sqlj\",\"jar_repository\",TRUE,FALSE)"); executeMetaDataFunction(stmt, "getPrimaryKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getProcedureColumns((String)null,\"sqlj\",\"install_jar\",(String)null)"); executeMetaDataFunction(stmt, "getProcedures((String)null,\"sqlj\",\"%\")"); executeMetaDataFunction(stmt, "getSchemas()"); executeMetaDataFunction(stmt, "getSuperTables((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getSuperTypes((String)null,\"sqlj\",\"%\")"); executeMetaDataFunction(stmt, "getTablePrivileges((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getTables((String)null,\"sqlj\",\"jar%\",{\"TABLE\"})"); executeMetaDataFunction(stmt, "getTableTypes()"); executeMetaDataFunction(stmt, "getTypeInfo()"); executeMetaDataFunction(stmt, "getUDTs((String)null,\"sqlj\",\"%\",(int[])null)"); executeMetaDataFunction(stmt, "getVersionColumns((String)null,\"sqlj\",\"jar_repository\")"); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } private void executeMetaDataFunction(Statement stmt, String functionCall) throws SQLException { ResultSet rs = null; try { System.out.println("*** " + functionCall + ":"); rs = stmt .executeQuery("SELECT * FROM javatest.callMetaDataMethod('" + functionCall + "')"); while(rs.next()) { System.out.println(rs.getString(1)); } rs.close(); } catch(Exception e) { System.out.println(" Failed: " + e.getMessage()); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } public void testResultSet() throws SQLException { String sql; Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { System.out.println("*** ResultSet test:"); sql = "SELECT * FROM javatest.executeSelect(" + "'select ''Foo'' as t_varchar, 1::integer as t_integer, " + "1.5::float as t_float, 23.67::decimal(8,2) as t_decimal, " + "''2005-06-01''::date as t_date, ''20:56''::time as t_time, " + "''2006-02-04 23:55:10''::timestamp as t_timestamp')"; rs = stmt.executeQuery(sql); System.out.println("SQL = " + sql); System.out.println("results:"); while(rs.next()) { System.out.println(rs.getString(1)); } rs.close(); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } }