/* * Copyright (c) 2004, PostgreSQL Global Development Group * See the LICENSE file in the project root for more information. */ package org.postgresql.test.jdbc2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.postgresql.jdbc.PgStatement; import org.postgresql.test.TestUtil; import org.postgresql.util.PSQLState; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /* * Test for getObject */ public class StatementTest { private Connection con; @Before public void setUp() throws Exception { con = TestUtil.openDB(); TestUtil.createTempTable(con, "test_statement", "i int"); TestUtil.createTempTable(con, "escapetest", "ts timestamp, d date, t time, \")\" varchar(5), \"\"\"){a}'\" text "); TestUtil.createTempTable(con, "comparisontest", "str1 varchar(5), str2 varchar(15)"); Statement stmt = con.createStatement(); stmt.executeUpdate(TestUtil.insertSQL("comparisontest", "str1,str2", "'_abcd','_found'")); stmt.executeUpdate(TestUtil.insertSQL("comparisontest", "str1,str2", "'%abcd','%found'")); stmt.close(); } @After public void tearDown() throws Exception { TestUtil.dropTable(con, "test_statement"); TestUtil.dropTable(con, "escapetest"); TestUtil.dropTable(con, "comparisontest"); con.close(); } @Test public void testClose() throws SQLException { Statement stmt = con.createStatement(); stmt.close(); try { stmt.getResultSet(); fail("statements should not be re-used after close"); } catch (SQLException ex) { } } /** * Closing a Statement twice is not an error. */ @Test public void testDoubleClose() throws SQLException { Statement stmt = con.createStatement(); stmt.close(); stmt.close(); } @Test public void testMultiExecute() throws SQLException { Statement stmt = con.createStatement(); assertTrue(stmt.execute("SELECT 1 as a; UPDATE test_statement SET i=1; SELECT 2 as b, 3 as c")); ResultSet rs = stmt.getResultSet(); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); rs.close(); assertTrue(!stmt.getMoreResults()); assertEquals(0, stmt.getUpdateCount()); assertTrue(stmt.getMoreResults()); rs = stmt.getResultSet(); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); rs.close(); assertTrue(!stmt.getMoreResults()); assertEquals(-1, stmt.getUpdateCount()); stmt.close(); } @Test public void testEmptyQuery() throws SQLException { Statement stmt = con.createStatement(); stmt.execute(""); assertNull(stmt.getResultSet()); assertTrue(!stmt.getMoreResults()); } @Test public void testUpdateCount() throws SQLException { Statement stmt = con.createStatement(); int count; count = stmt.executeUpdate("INSERT INTO test_statement VALUES (3)"); assertEquals(1, count); count = stmt.executeUpdate("INSERT INTO test_statement VALUES (3)"); assertEquals(1, count); count = stmt.executeUpdate("UPDATE test_statement SET i=4"); assertEquals(2, count); count = stmt.executeUpdate("CREATE TEMP TABLE another_table (a int)"); assertEquals(0, count); } @Test public void testEscapeProcessing() throws SQLException { Statement stmt = con.createStatement(); int count; count = stmt.executeUpdate("insert into escapetest (ts) values ({ts '1900-01-01 00:00:00'})"); assertEquals(1, count); count = stmt.executeUpdate("insert into escapetest (d) values ({d '1900-01-01'})"); assertEquals(1, count); count = stmt.executeUpdate("insert into escapetest (t) values ({t '00:00:00'})"); assertEquals(1, count); ResultSet rs = stmt.executeQuery("select {fn version()} as version"); assertTrue(rs.next()); // check nested and multiple escaped functions rs = stmt.executeQuery("select {fn version()} as version, {fn log({fn log(3.0)})} as log"); assertTrue(rs.next()); assertEquals(Math.log(Math.log(3)), rs.getDouble(2), 0.00001); stmt.executeUpdate("UPDATE escapetest SET \")\" = 'a', \"\"\"){a}'\" = 'b'"); // check "difficult" values rs = stmt.executeQuery("select {fn concat(')',escapetest.\")\")} as concat" + ", {fn concat('{','}')} " + ", {fn concat('''','\"')} " + ", {fn concat(\"\"\"){a}'\", '''}''')} " + " FROM escapetest"); assertTrue(rs.next()); assertEquals(")a", rs.getString(1)); assertEquals("{}", rs.getString(2)); assertEquals("'\"", rs.getString(3)); assertEquals("b'}'", rs.getString(4)); count = stmt.executeUpdate("create temp table b (i int)"); assertEquals(0, count); rs = stmt.executeQuery("select * from {oj test_statement a left outer join b on (a.i=b.i)} "); assertTrue(!rs.next()); // test escape escape character rs = stmt .executeQuery("select str2 from comparisontest where str1 like '|_abcd' {escape '|'} "); assertTrue(rs.next()); assertEquals("_found", rs.getString(1)); rs = stmt .executeQuery("select str2 from comparisontest where str1 like '|%abcd' {escape '|'} "); assertTrue(rs.next()); assertEquals("%found", rs.getString(1)); } @Test public void testPreparedFunction() throws SQLException { PreparedStatement pstmt = con.prepareStatement("SELECT {fn concat('a', ?)}"); pstmt.setInt(1, 5); ResultSet rs = pstmt.executeQuery(); assertTrue(rs.next()); assertEquals("a5", rs.getString(1)); } @Test public void testDollarInComment() throws SQLException { PreparedStatement pstmt = con.prepareStatement("SELECT /* $ */ {fn curdate()}"); ResultSet rs = pstmt.executeQuery(); assertTrue(rs.next()); assertNotNull("{fn curdate()} should be not null", rs.getString(1)); } @Test public void testDollarInCommentTwoComments() throws SQLException { PreparedStatement pstmt = con.prepareStatement("SELECT /* $ *//* $ */ {fn curdate()}"); ResultSet rs = pstmt.executeQuery(); assertTrue(rs.next()); assertNotNull("{fn curdate()} should be not null", rs.getString(1)); } @Test public void testNumericFunctions() throws SQLException { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select {fn abs(-2.3)} as abs "); assertTrue(rs.next()); assertEquals(2.3f, rs.getFloat(1), 0.00001); rs = stmt.executeQuery("select {fn acos(-0.6)} as acos "); assertTrue(rs.next()); assertEquals(Math.acos(-0.6), rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn asin(-0.6)} as asin "); assertTrue(rs.next()); assertEquals(Math.asin(-0.6), rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn atan(-0.6)} as atan "); assertTrue(rs.next()); assertEquals(Math.atan(-0.6), rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn atan2(-2.3,7)} as atan2 "); assertTrue(rs.next()); assertEquals(Math.atan2(-2.3, 7), rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn ceiling(-2.3)} as ceiling "); assertTrue(rs.next()); assertEquals(-2, rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn cos(-2.3)} as cos, {fn cot(-2.3)} as cot "); assertTrue(rs.next()); assertEquals(Math.cos(-2.3), rs.getDouble(1), 0.00001); assertEquals(1 / Math.tan(-2.3), rs.getDouble(2), 0.00001); rs = stmt.executeQuery("select {fn degrees({fn pi()})} as degrees "); assertTrue(rs.next()); assertEquals(180, rs.getDouble(1), 0.00001); rs = stmt.executeQuery("select {fn exp(-2.3)}, {fn floor(-2.3)}," + " {fn log(2.3)},{fn log10(2.3)},{fn mod(3,2)}"); assertTrue(rs.next()); assertEquals(Math.exp(-2.3), rs.getDouble(1), 0.00001); assertEquals(-3, rs.getDouble(2), 0.00001); assertEquals(Math.log(2.3), rs.getDouble(3), 0.00001); assertEquals(Math.log(2.3) / Math.log(10), rs.getDouble(4), 0.00001); assertEquals(1, rs.getDouble(5), 0.00001); rs = stmt.executeQuery("select {fn pi()}, {fn power(7,-2.3)}," + " {fn radians(-180)},{fn round(3.1294,2)}"); assertTrue(rs.next()); assertEquals(Math.PI, rs.getDouble(1), 0.00001); assertEquals(Math.pow(7, -2.3), rs.getDouble(2), 0.00001); assertEquals(-Math.PI, rs.getDouble(3), 0.00001); assertEquals(3.13, rs.getDouble(4), 0.00001); rs = stmt.executeQuery("select {fn sign(-2.3)}, {fn sin(-2.3)}," + " {fn sqrt(2.3)},{fn tan(-2.3)},{fn truncate(3.1294,2)}"); assertTrue(rs.next()); assertEquals(-1, rs.getInt(1)); assertEquals(Math.sin(-2.3), rs.getDouble(2), 0.00001); assertEquals(Math.sqrt(2.3), rs.getDouble(3), 0.00001); assertEquals(Math.tan(-2.3), rs.getDouble(4), 0.00001); assertEquals(3.12, rs.getDouble(5), 0.00001); } @Test public void testStringFunctions() throws SQLException { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select {fn ascii(' test')},{fn char(32)}" + ",{fn concat('ab','cd')}" + ",{fn lcase('aBcD')},{fn left('1234',2)},{fn length('123 ')}" + ",{fn locate('bc','abc')},{fn locate('bc','abc',3)}"); assertTrue(rs.next()); assertEquals(32, rs.getInt(1)); assertEquals(" ", rs.getString(2)); assertEquals("abcd", rs.getString(3)); assertEquals("abcd", rs.getString(4)); assertEquals("12", rs.getString(5)); assertEquals(3, rs.getInt(6)); assertEquals(2, rs.getInt(7)); assertEquals(0, rs.getInt(8)); rs = stmt.executeQuery( "SELECT {fn insert('abcdef',3,2,'xxxx')}" + ",{fn replace('abcdbc','bc','x')}"); assertTrue(rs.next()); assertEquals("abxxxxef", rs.getString(1)); assertEquals("axdx", rs.getString(2)); rs = stmt.executeQuery( "select {fn ltrim(' ab')},{fn repeat('ab',2)}" + ",{fn right('abcde',2)},{fn rtrim('ab ')}" + ",{fn space(3)},{fn substring('abcd',2,2)}" + ",{fn ucase('aBcD')}"); assertTrue(rs.next()); assertEquals("ab", rs.getString(1)); assertEquals("abab", rs.getString(2)); assertEquals("de", rs.getString(3)); assertEquals("ab", rs.getString(4)); assertEquals(" ", rs.getString(5)); assertEquals("bc", rs.getString(6)); assertEquals("ABCD", rs.getString(7)); } @Test public void testDateFuncWithParam() throws SQLException { // Prior to 8.0 there is not an interval + timestamp operator, // so timestampadd does not work. // PreparedStatement ps = con.prepareStatement( "SELECT {fn timestampadd(SQL_TSI_QUARTER, ? ,{fn now()})}, {fn timestampadd(SQL_TSI_MONTH, ?, {fn now()})} "); ps.setInt(1, 4); ps.setInt(2, 12); ResultSet rs = ps.executeQuery(); assertTrue(rs.next()); assertEquals(rs.getTimestamp(1), rs.getTimestamp(2)); } @Test public void testDateFunctions() throws SQLException { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select {fn curdate()},{fn curtime()}" + ",{fn dayname({fn now()})}, {fn dayofmonth({fn now()})}" + ",{fn dayofweek({ts '2005-01-17 12:00:00'})},{fn dayofyear({fn now()})}" + ",{fn hour({fn now()})},{fn minute({fn now()})}" + ",{fn month({fn now()})}" + ",{fn monthname({fn now()})},{fn quarter({fn now()})}" + ",{fn second({fn now()})},{fn week({fn now()})}" + ",{fn year({fn now()})} "); assertTrue(rs.next()); // ensure sunday =>1 and monday =>2 assertEquals(2, rs.getInt(5)); // Prior to 8.0 there is not an interval + timestamp operator, // so timestampadd does not work. // // second rs = stmt.executeQuery( "select {fn timestampdiff(SQL_TSI_SECOND,{fn now()},{fn timestampadd(SQL_TSI_SECOND,3,{fn now()})})} "); assertTrue(rs.next()); assertEquals(3, rs.getInt(1)); // MINUTE rs = stmt.executeQuery( "select {fn timestampdiff(SQL_TSI_MINUTE,{fn now()},{fn timestampadd(SQL_TSI_MINUTE,3,{fn now()})})} "); assertTrue(rs.next()); assertEquals(3, rs.getInt(1)); // HOUR rs = stmt.executeQuery( "select {fn timestampdiff(SQL_TSI_HOUR,{fn now()},{fn timestampadd(SQL_TSI_HOUR,3,{fn now()})})} "); assertTrue(rs.next()); assertEquals(3, rs.getInt(1)); // day rs = stmt.executeQuery( "select {fn timestampdiff(SQL_TSI_DAY,{fn now()},{fn timestampadd(SQL_TSI_DAY,-3,{fn now()})})} "); assertTrue(rs.next()); assertEquals(-3, rs.getInt(1)); // WEEK => extract week from interval is not supported by backend // rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_WEEK,{fn now()},{fn // timestampadd(SQL_TSI_WEEK,3,{fn now()})})} "); // assertTrue(rs.next()); // assertEquals(3,rs.getInt(1)); // MONTH => backend assume there are 0 month in an interval of 92 days... // rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_MONTH,{fn now()},{fn // timestampadd(SQL_TSI_MONTH,3,{fn now()})})} "); // assertTrue(rs.next()); // assertEquals(3,rs.getInt(1)); // QUARTER => backend assume there are 1 quater even in 270 days... // rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_QUARTER,{fn now()},{fn // timestampadd(SQL_TSI_QUARTER,3,{fn now()})})} "); // assertTrue(rs.next()); // assertEquals(3,rs.getInt(1)); // YEAR // rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_YEAR,{fn now()},{fn // timestampadd(SQL_TSI_YEAR,3,{fn now()})})} "); // assertTrue(rs.next()); // assertEquals(3,rs.getInt(1)); } @Test public void testSystemFunctions() throws SQLException { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select {fn ifnull(null,'2')}" + ",{fn user()} "); assertTrue(rs.next()); assertEquals("2", rs.getString(1)); assertEquals(TestUtil.getUser(), rs.getString(2)); rs = stmt.executeQuery("select {fn database()} "); assertTrue(rs.next()); assertEquals(TestUtil.getDatabase(), rs.getString(1)); } @Test public void testWarningsAreCleared() throws SQLException { Statement stmt = con.createStatement(); // Will generate a NOTICE: for primary key index creation stmt.execute("CREATE TEMP TABLE unused (a int primary key)"); stmt.executeQuery("SELECT 1"); // Executing another query should clear the warning from the first one. assertNull(stmt.getWarnings()); stmt.close(); } /** * The parser tries to break multiple statements into individual queries as required by the V3 * extended query protocol. It can be a little overzealous sometimes and this test ensures we keep * multiple rule actions together in one statement. */ @Test public void testParsingSemiColons() throws SQLException { Statement stmt = con.createStatement(); stmt.execute( "CREATE RULE r1 AS ON INSERT TO escapetest DO (DELETE FROM test_statement ; INSERT INTO test_statement VALUES (1); INSERT INTO test_statement VALUES (2); );"); stmt.executeUpdate("INSERT INTO escapetest(ts) VALUES (NULL)"); ResultSet rs = stmt.executeQuery("SELECT i from test_statement ORDER BY i"); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertTrue(!rs.next()); } @Test public void testParsingDollarQuotes() throws SQLException { // dollar-quotes are supported in the backend since version 8.0 Statement st = con.createStatement(); ResultSet rs; rs = st.executeQuery("SELECT '$a$ ; $a$'"); assertTrue(rs.next()); assertEquals("$a$ ; $a$", rs.getObject(1)); rs.close(); rs = st.executeQuery("SELECT $$;$$"); assertTrue(rs.next()); assertEquals(";", rs.getObject(1)); rs.close(); rs = st.executeQuery("SELECT $OR$$a$'$b$a$$OR$ WHERE '$a$''$b$a$'=$OR$$a$'$b$a$$OR$OR ';'=''"); assertTrue(rs.next()); assertEquals("$a$'$b$a$", rs.getObject(1)); assertFalse(rs.next()); rs.close(); rs = st.executeQuery("SELECT $B$;$b$B$"); assertTrue(rs.next()); assertEquals(";$b", rs.getObject(1)); rs.close(); rs = st.executeQuery("SELECT $c$c$;$c$"); assertTrue(rs.next()); assertEquals("c$;", rs.getObject(1)); rs.close(); rs = st.executeQuery("SELECT $A0$;$A0$ WHERE ''=$t$t$t$ OR ';$t$'=';$t$'"); assertTrue(rs.next()); assertEquals(";", rs.getObject(1)); assertFalse(rs.next()); rs.close(); st.executeQuery("SELECT /* */$$;$$/**//*;*/").close(); st.executeQuery("SELECT /* */--;\n$$a$$/**/--\n--;\n").close(); st.close(); } @Test public void testUnbalancedParensParseError() throws SQLException { Statement stmt = con.createStatement(); try { stmt.executeQuery("SELECT i FROM test_statement WHERE (1 > 0)) ORDER BY i"); fail("Should have thrown a parse error."); } catch (SQLException sqle) { } } @Test public void testExecuteUpdateFailsOnSelect() throws SQLException { Statement stmt = con.createStatement(); try { stmt.executeUpdate("SELECT 1"); fail("Should have thrown an error."); } catch (SQLException sqle) { } } @Test public void testExecuteUpdateFailsOnMultiStatementSelect() throws SQLException { Statement stmt = con.createStatement(); try { stmt.executeUpdate("/* */; SELECT 1"); fail("Should have thrown an error."); } catch (SQLException sqle) { } } @Test public void testSetQueryTimeout() throws SQLException { Statement stmt = con.createStatement(); long start = 0; boolean cancelReceived = false; try { stmt.setQueryTimeout(1); start = System.currentTimeMillis(); stmt.execute("select pg_sleep(10)"); } catch (SQLException sqle) { // state for cancel if ("57014".equals(sqle.getSQLState())) { cancelReceived = true; } } long duration = System.currentTimeMillis() - start; if (!cancelReceived || duration > 5000) { fail("Query should have been cancelled since the timeout was set to 1 sec." + " Cancel state: " + cancelReceived + ", duration: " + duration); } } /** * Test executes two queries one after another. The first one has timeout of 1ms, and the second * one does not. The timeout of the first query should not impact the second one. */ @Test public void testShortQueryTimeout() throws SQLException { long deadLine = System.currentTimeMillis() + 10000; Statement stmt = con.createStatement(); ((PgStatement) stmt).setQueryTimeoutMs(1); Statement stmt2 = con.createStatement(); while (System.currentTimeMillis() < deadLine) { try { stmt.execute("select 1"); } catch (SQLException e) { // ignore "statement cancelled" } stmt2.executeQuery("select 1"); } } @Test public void testSetQueryTimeoutWithSleep() throws SQLException, InterruptedException { // check that the timeout starts ticking at execute, not at the // setQueryTimeout call. Statement stmt = con.createStatement(); try { stmt.setQueryTimeout(1); Thread.sleep(3000); stmt.execute("select pg_sleep(5)"); fail("statement should have been canceled by query timeout"); } catch (SQLException sqle) { // state for cancel if (sqle.getSQLState().compareTo("57014") != 0) { throw sqle; } } } @Test public void testSetQueryTimeoutOnPrepared() throws SQLException, InterruptedException { // check that a timeout set on a prepared statement works on every // execution. PreparedStatement pstmt = con.prepareStatement("select pg_sleep(5)"); pstmt.setQueryTimeout(1); for (int i = 1; i <= 3; i++) { try { ResultSet rs = pstmt.executeQuery(); fail("statement should have been canceled by query timeout (execution #" + i + ")"); } catch (SQLException sqle) { // state for cancel if (sqle.getSQLState().compareTo("57014") != 0) { throw sqle; } } } } @Test public void testSetQueryTimeoutWithoutExecute() throws SQLException, InterruptedException { // check that a timeout set on one statement doesn't affect another Statement stmt1 = con.createStatement(); stmt1.setQueryTimeout(1); Statement stmt2 = con.createStatement(); ResultSet rs = stmt2.executeQuery("SELECT pg_sleep(2)"); } @Test public void testResultSetTwice() throws SQLException { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select {fn abs(-2.3)} as abs "); assertNotNull(rs); ResultSet rsOther = stmt.getResultSet(); assertNotNull(rsOther); } @Test public void testMultipleCancels() throws Exception { org.postgresql.util.SharedTimer sharedTimer = org.postgresql.Driver.getSharedTimer(); Connection connA = null; Connection connB = null; Statement stmtA = null; Statement stmtB = null; ResultSet rsA = null; ResultSet rsB = null; try { assertEquals(0, sharedTimer.getRefCount()); connA = TestUtil.openDB(); connB = TestUtil.openDB(); stmtA = connA.createStatement(); stmtB = connB.createStatement(); stmtA.setQueryTimeout(1); stmtB.setQueryTimeout(1); try { rsA = stmtA.executeQuery("SELECT pg_sleep(2)"); } catch (SQLException e) { // ignore the expected timeout } assertEquals(1, sharedTimer.getRefCount()); try { rsB = stmtB.executeQuery("SELECT pg_sleep(2)"); } catch (SQLException e) { // ignore the expected timeout } } finally { TestUtil.closeQuietly(rsA); TestUtil.closeQuietly(rsB); TestUtil.closeQuietly(stmtA); TestUtil.closeQuietly(stmtB); TestUtil.closeQuietly(connA); TestUtil.closeQuietly(connB); } assertEquals(0, sharedTimer.getRefCount()); } /** * Tests that calling {@code java.sql.Statement#close()} from a concurrent thread does not result * in {@link java.util.ConcurrentModificationException} */ @Test public void testSideStatementFinalizers() throws SQLException { long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(2); final AtomicInteger leaks = new AtomicInteger(); final AtomicReference<Throwable> cleanupFailure = new AtomicReference<Throwable>(); for (int q = 0; System.currentTimeMillis() < deadline || leaks.get() < 10000; q++) { for (int i = 0; i < 100; i++) { PreparedStatement ps = con.prepareStatement("select " + (i + q)); ps.close(); } final int nextId = q; new Object() { PreparedStatement ps = con.prepareStatement("select /*leak*/ " + nextId); @Override protected void finalize() throws Throwable { super.finalize(); try { ps.close(); } catch (Throwable t) { cleanupFailure.compareAndSet(null, t); } leaks.incrementAndGet(); } }; } if (cleanupFailure.get() != null) { throw new IllegalStateException("Detected failure in cleanup thread", cleanupFailure.get()); } } /** * Test that $JAVASCRIPT$ protects curly braces from JDBC {fn now()} kind of syntax. * @throws SQLException if something goes wrong */ @Test public void testJavascriptFunction() throws SQLException { String str = " var _modules = {};\n" + " var _current_stack = [];\n" + "\n" + " // modules start\n" + " _modules[\"/root/aidbox/fhirbase/src/core\"] = {\n" + " init: function(){\n" + " var exports = {};\n" + " _current_stack.push({file: \"core\", dir: \"/root/aidbox/fhirbase/src\"})\n" + " var module = {exports: exports};"; PreparedStatement ps = null; try { ps = con.prepareStatement("select $JAVASCRIPT$" + str + "$JAVASCRIPT$"); ResultSet rs = ps.executeQuery(); rs.next(); assertEquals("Javascript code has been protected with $JAVASCRIPT$", str, rs.getString(1)); } finally { TestUtil.closeQuietly(ps); } } @Test public void testUnterminatedDollarQuotes() throws SQLException { ensureSyntaxException("dollar quotes", "CREATE OR REPLACE FUNCTION update_on_change() RETURNS TRIGGER AS $$\n" + "BEGIN"); } @Test public void testUnterminatedNamedDollarQuotes() throws SQLException { ensureSyntaxException("dollar quotes", "CREATE OR REPLACE FUNCTION update_on_change() RETURNS TRIGGER AS $ABC$\n" + "BEGIN"); } @Test public void testUnterminatedComment() throws SQLException { ensureSyntaxException("block comment", "CREATE OR REPLACE FUNCTION update_on_change() RETURNS TRIGGER AS /* $$\n" + "BEGIN $$"); } @Test public void testUnterminatedLiteral() throws SQLException { ensureSyntaxException("string literal", "CREATE OR REPLACE FUNCTION update_on_change() 'RETURNS TRIGGER AS $$\n" + "BEGIN $$"); } @Test public void testUnterminatedIdentifier() throws SQLException { ensureSyntaxException("string literal", "CREATE OR REPLACE FUNCTION \"update_on_change() RETURNS TRIGGER AS $$\n" + "BEGIN $$"); } private void ensureSyntaxException(String errorType, String sql) throws SQLException { PreparedStatement ps = null; try { ps = con.prepareStatement(sql); ps.executeUpdate(); fail("Query with unterminated " + errorType + " should fail"); } catch (SQLException e) { assertEquals("Query should fail with unterminated " + errorType, PSQLState.SYNTAX_ERROR.getState(), e.getSQLState()); } finally { TestUtil.closeQuietly(ps); } } }