/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 * Modified by
 * @author  Data Virtuality GmbH
 * @date    $Date$
 *  
 */
/*
 * EXTENSION
 * ISSUE: DVCORE-2521
 * FIXED BY: Roman Pedchenko
 * DATE: 2014-12-05
 * ADOPTED FROM: TEIID-2153, 2171, 2359, 2149, 2358, 2410, 2271, 2467, 2429, 2363, 2160, 2461, 2797, 2326, 1557
 * END EXTENSION
 */
// https://issues.jboss.org/browse/TEIID-2930
// https://issues.jboss.org/browse/TEIID-2444
// https://issues.jboss.org/browse/TEIID-3487
// https://issues.jboss.org/browse/TEIID-4122
// https://issues.jboss.org/browse/TEIID-3568
// https://issues.jboss.org/browse/TEIID-2610
// https://issues.jboss.org/browse/TEIID-2476
// https://issues.jboss.org/browse/TEIID-2260
// https://issues.jboss.org/browse/TEIID-247, 2166, 2151, 2234, 2581, 2571, 2369, 1386, 2226, 2233, 2224, 2225, 2257
// https://issues.redhat.com/browse/TEIID-3177
// https://github.com/teiid/teiid/commit/45e4ed954cc1b677d55672afec64a354f1eef582
// https://issues.redhat.com/browse/TEIID-3818
// https://github.com/teiid/teiid/commit/72abc34aa9796cf19def8364683dd2dea4805fbc
// https://issues.redhat.com/browse/TEIID-2228
// https://issues.redhat.com/browse/TEIID-2929
// https://issues.redhat.com/browse/TEIID-3362
// https://issues.redhat.com/browse/TEIID-4446
// https://issues.redhat.com/browse/TEIID-5499
// https://issues.redhat.com/browse/TEIID-4866 (partially)
package org.teiid.transport;

import static org.junit.jupiter.api.Assertions.*;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.sql.Array;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.postgresql.Driver;
import org.teiid.adminapi.Model.Type;
import org.teiid.adminapi.impl.ModelMetaData;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.common.buffer.BufferManagerFactory;
import org.teiid.core.util.ObjectConverterUtil;
import org.teiid.core.util.UnitTestUtil;
import org.teiid.deployers.VDBRepository;
import org.teiid.jdbc.FakeServer;
import org.teiid.jdbc.TestMMDatabaseMetaData;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.MetadataStore;
import org.teiid.query.parser.QueryParser;

@SuppressWarnings("nls")
public class TestODBCSocketTransport {

	enum Mode {
		LEGACY,//how the test was originally written
		ENABLED,
		LOGIN,
		DISABLED
	}
	
	static class FakeOdbcServer {
		InetSocketAddress addr;
		ODBCSocketListener odbcTransport;
		FakeServer server;
		
		public void start(Mode mode) throws Exception {
			SocketConfiguration config = new SocketConfiguration();
			SSLConfiguration sslConfig = new SSLConfiguration();
			if (mode == Mode.LOGIN) {
				sslConfig.setMode(SSLConfiguration.LOGIN);
			} else if (mode == Mode.ENABLED || mode == Mode.LEGACY) {
				sslConfig.setMode(SSLConfiguration.ENABLED);
				sslConfig.setAuthenticationMode(SSLConfiguration.ONEWAY);
				sslConfig.setKeystoreFilename(UnitTestUtil.getTestDataFile("keystore.jks").getAbsolutePath());
				sslConfig.setKeystorePassword("password");
			} else {
				sslConfig.setMode(SSLConfiguration.DISABLED);
			}
			config.setSSLConfiguration(sslConfig);
			addr = new InetSocketAddress(0);
			config.setBindAddress(addr.getHostName());
			config.setPortNumber(addr.getPort());
			server = new FakeServer(true);
			LogonImpl logon = Mockito.mock(LogonImpl.class);
			odbcTransport = new ODBCSocketListener(addr, config, Mockito.mock(ClientServiceRegistryImpl.class), BufferManagerFactory.getStandaloneBufferManager(), 100000, logon, server.getDriver());
			odbcTransport.setMaxBufferSize(1000); //set to a small size to ensure buffering over the limit works
			if (mode == Mode.LEGACY) {
				odbcTransport.setRequireSecure(false);
			}
			server.deployVDB("parts", UnitTestUtil.getTestDataPath() + "/PartsSupplier.vdb");
		}
		
		public void stop() {
			server.stop();
			odbcTransport.stop();
		}
	}
	
	@Test public void testArray() throws Exception {
		Statement s = conn.createStatement();
		ResultSet rs = s.executeQuery("select (1,2)");
		rs.next();
		Array result = rs.getArray(1);
		ResultSet rs1 = result.getResultSet();
		rs1.next();
		assertEquals(1, rs1.getInt(1));
	}

	@Test public void testVDBConnectionProperty() throws Exception {
		VDBMetaData vdb = new VDBMetaData();
		vdb.setName("x");
		vdb.addProperty("connection.foo", "bar");
		ModelMetaData mmd = new ModelMetaData();
		mmd.setName("x");
		mmd.setSchemaSourceType("ddl");
		mmd.setModelType(Type.VIRTUAL);
		String ddl = "create view v as select 1";
		mmd.setSchemaText(ddl);
		/*vdb.addModel(mmd);
		odbcServer.server.deployVDB(vdb);*/
		VDBRepository vdbRepository = new VDBRepository();
		MetadataFactory mf = new MetadataFactory("vdb", 1, vdbRepository.getRuntimeTypeMap(), mmd);
		QueryParser.getQueryParser().parseDDL(mf, ddl);
		MetadataStore ms = mf.asMetadataStore();
		odbcServer.server.deployVDB(vdb, ms, new FakeServer.DeployVDBParameter(null, null));
		this.conn.close();
		connect("x");
		Statement s = conn.createStatement();
		assertTrue(s.execute("show foo"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		String value = rs.getString(1);
		assertEquals("bar", value);
	}

	@Test public void testDecimalPrecision() throws Exception {
		VDBMetaData vdb = new VDBMetaData();
		vdb.setName("decimal");
		vdb.addProperty("connection.foo", "bar");
		ModelMetaData mmd = new ModelMetaData();
		mmd.setName("x");
		mmd.setModelType(Type.VIRTUAL);
		String ddl = "create view v (x decimal(2), y decimal) as select 1.0, 2.0";
		mmd.addSourceMetadata("ddl", ddl);
		/*vdb.addModel(mmd);
		odbcServer.server.deployVDB(vdb);*/
		VDBRepository vdbRepository = new VDBRepository();
		MetadataFactory mf = new MetadataFactory("vdb", 1, vdbRepository.getRuntimeTypeMap(), mmd);
		QueryParser.getQueryParser().parseDDL(mf, ddl);
		MetadataStore ms = mf.asMetadataStore();
		odbcServer.server.deployVDB(vdb, ms, new FakeServer.DeployVDBParameter(null, null));
		this.conn.close();
		connect("decimal");
		Statement s = conn.createStatement();
		s.execute("select * from v");
		ResultSetMetaData metadata = s.getResultSet().getMetaData();
		assertEquals(2, metadata.getPrecision(1));
		assertEquals(0, metadata.getScale(1));

		assertEquals(32767, metadata.getPrecision(2));
		assertEquals(16383, metadata.getScale(2));

		s.execute("select atttypmod from pg_attribute where attname = 'y'");
		s.getResultSet().next();
		assertEquals(2147434499, s.getResultSet().getInt(1));
	}
	
	private static FakeOdbcServer odbcServer = new FakeOdbcServer();
	
	@BeforeAll public static void oneTimeSetup() throws Exception {
		odbcServer.start(Mode.LEGACY);
	}
	
	@AfterAll public static void oneTimeTearDown() throws Exception {
		odbcServer.stop();
	}
	
	Connection conn;

	@BeforeEach public void setUp() throws Exception {
		String database = "parts";
		connect(database);
	}

	private void connect(String database) throws SQLException {
		Driver d = new Driver();
		Properties p = new Properties();
		p.setProperty("user", "testuser");
		p.setProperty("password", "testpassword");
		conn = d.connect("jdbc:postgresql://"+odbcServer.addr.getHostName()+":" +odbcServer.odbcTransport.getPort()+"/"+database, p);
	}
	
	@AfterEach public void tearDown() throws Exception {
		if (conn != null) {
			conn.close();
		}
	}
	
	/**
	 * Under the covers this still executes a prepared statement due to the driver handling
	 */
    @Test public void testSelect() throws Exception {
		Statement s = conn.createStatement();
		assertTrue(s.execute("select * from tables order by name"));
		//TestMMDatabaseMetaData.compareResultSet(s.getResultSet());
	}
	
    @Test public void testTransactionalMultibatch() throws Exception {
		Statement s = conn.createStatement();
		conn.setAutoCommit(false);
		assertTrue(s.execute("select tables.name from tables, columns limit 1025"));
		int count = 0;
		while (s.getResultSet().next()) {
			count++;
		}
		assertEquals(1025, count);
		conn.setAutoCommit(true);
	}
	
	@Test public void testMultibatchSelect() throws Exception {
		Statement s = conn.createStatement();
		assertTrue(s.execute("select * from tables, columns limit 7000"));
		ResultSet rs = s.getResultSet();
		int i = 0;
		while (rs.next()) {
			i++;
			rs.getString(1);
		}
		// BUG: wrong number of rows in expected and generated result. The new result (8712) includes
		// our new system tables.
		// --> Previous version: <-- 
		// #Tables  = 26 + 5 = 31 (System VDB + PartsSupplier VDB)
		// #Columns = 235 + 17 = 252 (System VDB + PartsSupplier VDB)
		// #Cartesian Product = 31 * 252 = 7812
		// --> Current version: <-- 
		// #Tables  = 28 + 5 = 33 (System VDB + PartsSupplier VDB)
		// #Columns = 247 + 17 = 264 (System VDB + PartsSupplier VDB)
		// #Cartesian Product = 33 * 264 = 8712
		// ISSUE: DV-28
		// SOLUTION changed the expected value
		// FIXED by Salvatore Raunich
		// date 07.08.2012
//		assertEquals(7812, i);
//		assertEquals(8712, i);
		// updated 10.08.2012
		// #Tables  = 29 + 5 = 34 (System VDB + PartsSupplier VDB)
		// #Columns = 249 + 17 = 266 (System VDB + PartsSupplier VDB)
		// #Cartesian Product = 34 * 266 = 9044
//		assertEquals(9044, i);
		// updated 08.09.2012
		// #Tables  = 29 + 5 = 34 (System VDB + PartsSupplier VDB)
		// #Columns = 250 + 17 = 267 (System VDB + PartsSupplier VDB)
		// #Cartesian Product = 34 * 267 = 9078
//		assertEquals(9078, i);
		assertEquals(7000, i);
		// BUG END
	}

	/**
	 * tests that the portal max is handled correctly
	 */
	@Test public void testMultibatchSelectPrepared() throws Exception {
		PreparedStatement s = conn.prepareStatement("select * from tables t1, tables t2 where t1.name > ?");
		conn.setAutoCommit(false);
		s.setFetchSize(100);
		s.setString(1, "a");
		ResultSet rs = s.executeQuery();
		int i = 0;
		while (rs.next()) {
			i++;
			rs.getString(1);
		}
		assertEquals(100, i);
	}
	
	@Test public void testBlob() throws Exception {
		Statement s = conn.createStatement();
		assertTrue(s.execute("select to_bytes('abc', 'UTF-16')"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		byte[] bytes = rs.getBytes(1);
		assertEquals("abc", new String(bytes, Charset.forName("UTF-16")));
	}
	
	@Test public void testClob() throws Exception {
		Statement s = conn.createStatement();
		assertTrue(s.execute("select cast('abc' as clob)"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		//getting as a clob is unsupported, since it uses the lo logic
		String clob = rs.getString(1);
		assertEquals("abc", clob);
	}
	
	@Test public void testLargeClob() throws Exception {
		Statement s = conn.createStatement();
		assertTrue(s.execute("select cast(repeat('_', 3000) as clob)"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		//getting as a clob is unsupported, since it uses the lo logic
		String clob = rs.getString(1);
		assertEquals(3000, clob.length());
	}
	
	@Test public void testMultiRowBuffering() throws Exception {
		Statement s = conn.createStatement();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 11; i++) {
			sb.append("select '' union all ");
		}
		sb.append("select ''");
		assertTrue(s.execute(sb.toString()));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		String str = rs.getString(1);
		assertEquals(0, str.length());
	}

	@Test public void testTransactionCycle() throws Exception {
		//TODO: drill in to ensure that the underlying statement has been set to autocommit false
		conn.setAutoCommit(false); 
		Statement s = conn.createStatement();
		assertTrue(s.execute("select * from tables order by name"));
		conn.setAutoCommit(true);
	}
	
	@Test public void testPk() throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select ta.attname, ia.attnum, ic.relname, n.nspname, tc.relname " +//$NON-NLS-1$
			"from pg_catalog.pg_attribute ta, pg_catalog.pg_attribute ia, pg_catalog.pg_class tc, pg_catalog.pg_index i, " +//$NON-NLS-1$
			"pg_catalog.pg_namespace n, pg_catalog.pg_class ic where tc.relname = E'pg_attribute' AND n.nspname = E'pg_catalog'");
		TestMMDatabaseMetaData.compareResultSet(rs);
	}
	
	@Test public void testPkPrepared() throws Exception {
		PreparedStatement stmt = conn.prepareStatement("select ta.attname, ia.attnum, ic.relname, n.nspname, tc.relname " +//$NON-NLS-1$
				"from pg_catalog.pg_attribute ta, pg_catalog.pg_attribute ia, pg_catalog.pg_class tc, pg_catalog.pg_index i, " +//$NON-NLS-1$
				"pg_catalog.pg_namespace n, pg_catalog.pg_class ic where tc.relname = E'pg_attribute' AND n.nspname = E'pg_catalog'");
		ResultSet rs = stmt.executeQuery();
		TestMMDatabaseMetaData.compareResultSet(rs);
	}	
	
	@Test public void testColumnMetadataWithAlias() throws Exception {
		PreparedStatement stmt = conn.prepareStatement("select ta.attname as x from pg_catalog.pg_attribute ta limit 1");
		ResultSet rs = stmt.executeQuery();
		TestMMDatabaseMetaData.compareResultSet(rs);
	}	
	
	@Test public void testPreparedError() throws Exception {
		PreparedStatement stmt = conn.prepareStatement("select cast(? as integer)");
		stmt.setString(1, "a");
		try {
			stmt.executeQuery();
		} catch (SQLException e) {
			assertTrue(e.getMessage().contains("Error converting"));
		}
	}
	
	@Test public void testPreparedError1() throws Exception {
		PreparedStatement stmt = conn.prepareStatement("select");
		try {
			stmt.executeQuery();
		} catch (SQLException e) {
			assertTrue(e.getMessage().contains("Parsing error"));
		}
	}
	
	@Test public void testEscapedLiteral() throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select E'\\n\\thello pg'");
		assertTrue(rs.next());
		assertEquals("\n\thello pg", rs.getString(1));
	}
	
	@Test public void testPgProc() throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select * from pg_proc");
		rs.next();
		assertEquals("oid", rs.getArray("proargtypes").getBaseTypeName());
		TestMMDatabaseMetaData.compareResultSet(rs); //compare the rest
	}
	
	// this does not work as JDBC always sends the queries in prepared form
	// it should be fixed in TEIID-2612
	public void testPgDeclareCursor() throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("begin;declare \"foo\" cursor for select * from pg_proc;fetch 10 in \"foo\"; close \"foo\"");
		rs.next();		
	}

	// this does not work as JDBC always sends the queries in prepared form
	// it should be fixed in TEIID-2612
	public void testScrollCursor() throws Exception {
		Statement stmt = conn.createStatement();
		//ExtendedQueryExectutorImpl.simplePortal = "foo";
		try {
			assertFalse(stmt.execute("declare \"foo\" insensitive scroll cursor for select * from pg_proc;"));
			assertFalse(stmt.execute("move 5 in \"foo\""));
			stmt.execute("fetch 10 in \"foo\"");
			ResultSet rs = stmt.getResultSet();
			int rowCount = 0;
			while (rs.next()) {
				rowCount++;
			}
			assertEquals(6, rowCount);
			stmt.execute("close \"foo\"");
		} finally {
			//ExtendedQueryExectutorImpl.simplePortal = null;
		}

	}

	// this does not work as JDBC always sends the queries in prepared form
	// it should be fixed in TEIID-2612
	public void testScrollCursorWithHold() throws Exception {
		Statement stmt = conn.createStatement();
		//ExtendedQueryExectutorImpl.simplePortal = "foo";
		try {
			assertFalse(stmt.execute("declare \"foo\" insensitive scroll cursor with hold for select * from pg_proc;"));
			assertFalse(stmt.execute("move 5 in \"foo\""));
			stmt.execute("fetch 10 in \"foo\"");
			ResultSet rs = stmt.getResultSet();
			int rowCount = 0;
			while (rs.next()) {
				rowCount++;
			}
			assertEquals(7, rowCount);
			stmt.execute("close \"foo\"");
		} finally {
			//ExtendedQueryExectutorImpl.simplePortal = null;
		}

	}
	
	@Test public void testPgProcedure() throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select has_function_privilege(100, 'foo')");
		rs.next();
	}
	
	@Test public void testPreparedUpdate() throws Exception {
		Statement stmt = conn.createStatement();
		assertFalse(stmt.execute("create local temporary table x (y string)"));
		PreparedStatement ps = conn.prepareStatement("delete from x");
		assertFalse(ps.execute());
		assertNull(ps.getMetaData());
	}
	
    @Test public void testSelectSsl() throws Exception {
		conn.close();
		Driver d = new Driver();
		Properties p = new Properties();
		p.setProperty("user", "testuser");
		p.setProperty("password", "testpassword");
		p.setProperty("ssl", "true");
	    p.setProperty("sslfactory", "org.postgresql.ssl.NonValidatingFactory");
		conn = d.connect("jdbc:postgresql://"+odbcServer.addr.getHostName()+":" +odbcServer.odbcTransport.getPort()+"/parts", p);
		Statement s = conn.createStatement();
		assertTrue(s.execute("select * from tables order by name"));
		//TestMMDatabaseMetaData.compareResultSet("TestODBCSocketTransport/testSelect", s.getResultSet());
	}
	
	@Test public void testPayload() throws Exception {
		Statement s = conn.createStatement();
		assertFalse(s.execute("SET PAYLOAD x y"));
		assertTrue(s.execute("SELECT commandpayload('x')"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		String str = rs.getString(1);
		assertEquals("y", str);
	}
	
	@Test public void testShowPlan() throws Exception {
		Statement s = conn.createStatement();
		assertFalse(s.execute("SET SHOWPLAN ON"));
		assertTrue(s.execute("SELECT 1"));
		assertTrue(s.execute("SHOW PLAN"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		String str = rs.getString(1);
		assertTrue(str.startsWith("ProjectNode\n  + Output Columns:expr1 (integer)\n  + Statistics:\n    0: Node Output Rows: 1"));
	}
	
	@Test public void testSetEmptyLiteral() throws Exception {
		Statement s = conn.createStatement();
		assertFalse(s.execute("SET min_client_messages TO ''"));
		assertTrue(s.execute("SHOW min_client_messages"));
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		assertEquals("", rs.getString(1));
	}
	
	@Test public void testColons() throws Exception {
		Statement s = conn.createStatement();
		//make sure that we aren't mishandling the ::
		ResultSet rs = s.executeQuery("select 'a::b'");
		assertTrue(rs.next());
		assertEquals("a::b", rs.getString(1));
		
		rs = s.executeQuery("select ' a::b'");
		assertTrue(rs.next());
		assertEquals(" a::b", rs.getString(1));
		
		rs = s.executeQuery("select name::varchar from tables where name = 'Columns'");
		assertTrue(rs.next());
		assertEquals("Columns", rs.getString(1));
	}

	@Test public void testInt2Vector() throws Exception {
		Statement s = conn.createStatement();
		ResultSet rs = s.executeQuery("select indkey FROM pg_index order by oid");
		TestMMDatabaseMetaData.compareResultSet(rs);
	}

	@Test public void test_pg_client_encoding() throws Exception {
		Statement s = conn.createStatement();
		ResultSet rs = s.executeQuery("select pg_client_encoding()");
		rs.next();
		// Default encoding is reported as "UTF8" or "UNICODE" depending on the OS/JVM locale
		String defaultEncoding = rs.getString(1);
		assertTrue("UTF8".equals(defaultEncoding) || "UNICODE".equals(defaultEncoding),
				"Expected UTF8 or UNICODE but was: " + defaultEncoding);

		s.execute("set client_encoding UTF8");
		rs = s.executeQuery("select pg_client_encoding()");
		rs.next();
		assertEquals("UTF8", rs.getString(1));
	}

	/**
	 * TODO: we really want an odbc test, but this confirms the pg_description table and ~ rewrite handling
	 * @throws Exception
	 */
	@Test public void test_table_with_underscore() throws Exception {
		DatabaseMetaData metadata = conn.getMetaData();
		System.out.println(metadata.getSearchStringEscape());
		ResultSet rs = metadata.getTables(null, null, "pg_index", null);
		assertTrue(rs.next());
	}

	/**
	 * Ensures that the client is notified about the change.  However the driver will
	 * throw an exception as it requires UTF8
	 * @throws Exception
	 */
	@Test public void test_pg_client_encoding1() throws Exception {
		Statement s = conn.createStatement();
		assertThrows(SQLException.class, () -> s.execute("set client_encoding LATIN1"));
	}

	@Test public void testImplicitPortalClosing() throws Exception {
		Statement statement = conn.createStatement();
		ResultSet rs = statement.executeQuery("select session_id()");
		rs.next();
		String id = rs.getString(1);
		statement.close();

		PreparedStatement s = conn.prepareStatement("select 1");
		s.executeQuery();

		assertEquals(1, odbcServer.server.getDqp().getRequestsForSession(id).size());

		s.executeQuery();

		assertEquals(1, odbcServer.server.getDqp().getRequestsForSession(id).size());
    }
	@Test public void testExportedKey() throws Exception {
		String sql = ObjectConverterUtil.convertFileToString(UnitTestUtil.getTestDataFile("exported-fk-query.txt"));

		Statement s = conn.createStatement();
		s.execute(sql);
		ResultSet rs = s.getResultSet();
		assertTrue(rs.next());
		assertEquals("STATUS_ID", rs.getString(4));
		assertFalse(rs.next());
	}

	@Test public void testRegClass() throws Exception {
		Statement s = conn.createStatement();
		ResultSet rs = s.executeQuery("select '\"pg_catalog.pg_class\"'::regclass");
		rs.next();
		int oid = rs.getInt(1);
		rs = s.executeQuery("select oid from pg_class where relname='pg_class'");
		rs.next();
		int oid1 = rs.getInt(1);
		assertEquals(oid, oid1);
	}

	@Test public void testGeometry() throws Exception {
		Statement s = conn.createStatement();
		s.execute("SELECT ST_GeomFromText('POLYGON ((40 0, 50 50, 0 50, 0 0, 40 0))')");
		ResultSet rs = s.getResultSet();
		rs.next();
		ResultSetMetaData rsmd = rs.getMetaData();
		assertEquals("geometry", rsmd.getColumnTypeName(1));
		assertEquals("00200000030000000000000001000000054044000000000000000000000000000040490000000000004049000000000000000000000000000040490000000000000000000000000000000000000000000040440000000000000000000000000000", rs.getString(1));
	}

	@Test public void testGeography() throws Exception {
		Statement s = conn.createStatement();
		s.execute("SELECT ST_GeogFromText('POLYGON ((40 0, 50 50, 0 50, 0 0, 40 0))')");
		ResultSet rs = s.getResultSet();
		rs.next();
		ResultSetMetaData rsmd = rs.getMetaData();
		assertEquals("geography", rsmd.getColumnTypeName(1));
		assertEquals("java.lang.Object", rsmd.getColumnClassName(1));
		assertEquals("0020000003000010E600000001000000054044000000000000000000000000000040490000000000004049000000000000000000000000000040490000000000000000000000000000000000000000000040440000000000000000000000000000", rs.getString(1));
	}

	@Test public void testJson() throws Exception {
		Statement s = conn.createStatement();
		s.execute("SELECT jsonParse('[1,2]', false);");
		ResultSet rs = s.getResultSet();
		rs.next();
		ResultSetMetaData rsmd = rs.getMetaData();
		//assertEquals("text", rsmd.getColumnTypeName(1));
		assertEquals("json", rsmd.getColumnTypeName(1));
		//assertEquals("org.postgresql.util.PGobject", rsmd.getColumnClassName(1));
		//assertEquals("java.lang.String", rsmd.getColumnClassName(1));
		assertEquals("java.lang.Object", rsmd.getColumnClassName(1));
		assertEquals("[1,2]", rs.getString(1));
	}

	/*@Test public void testConstraintDef() throws Exception {
		Statement s = conn.createStatement();
		s.execute("SELECT pg_get_constraintdef((select oid from pg_constraint where contype = 'f' and conrelid = (select oid from pg_class where relname = 'Functions')), true)");
		ResultSet rs = s.getResultSet();
		rs.next();
		assertEquals("FOREIGN KEY (VDBName,SchemaName) REFERENCES SYS.Schemas(VDBName,Name)", rs.getString(1));
	}*/

	@Test public void testXML() throws Exception {
		Statement s = conn.createStatement();
		s.execute("SELECT xmlelement(name x)");
		ResultSet rs = s.getResultSet();
		rs.next();
		ResultSetMetaData rsmd = rs.getMetaData();
		assertEquals("xml", rsmd.getColumnTypeName(1));
		assertEquals("<x></x>", rs.getString(1));
	}
}
