From eec11d2efbb5a6499e1f651f437c44c13b80b4fd Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 12:54:10 +0530 Subject: [PATCH 01/16] HIVE-28265: Fix JDBC timeout message for hive.query.timeout.seconds Use server-side HiveSQLException with effective timeout seconds before cancel(TIMEDOUT) so GetOperationStatus exposes correct errorMessage. JDBC: prefer server message; ignore bogus 'after 0 seconds'; fall back to Statement queryTimeout or last SET hive.query.timeout.seconds tracked on HiveConnection. Parse SET assignments anywhere in SQL (last wins). SQLOperation async: do not overwrite operationException when already TIMEDOUT. Tests: TestJdbcDriver2 (session SET + sleep UDF; tighten testQueryTimeout). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 35 ++++++++++++ .../org/apache/hive/jdbc/HiveConnection.java | 22 ++++++++ .../org/apache/hive/jdbc/HiveStatement.java | 53 ++++++++++++++++++- .../service/cli/operation/SQLOperation.java | 13 ++++- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 6947b0b4b88b..85a477632288 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -2662,6 +2662,10 @@ public void testQueryTimeout() throws Exception { fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { assertNotNull(e); + assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), + e.getMessage().contains("1")); + assertFalse("Message should not claim 0 seconds: " + e.getMessage(), + e.getMessage().contains("after 0 seconds")); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2680,6 +2684,37 @@ public void testQueryTimeout() throws Exception { stmt.close(); } + /** + * HIVE-28265: hive.query.timeout.seconds drives the server-side timer, but the JDBC client + * must not report "0 seconds" when Statement#setQueryTimeout was not used. + */ + @Test + public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + String udfName = SleepMsUDF.class.getName(); + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + + Statement stmt = con.createStatement(); + stmt.execute("set hive.query.timeout.seconds=1s"); + + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertNotNull(e); + assertTrue("Message should include session timeout (1s): " + e.getMessage(), + e.getMessage().contains("1")); + assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), + e.getMessage().contains("after 0 seconds")); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + } + stmt.close(); + } + /** * Test the non-null value of the Yarn ATS GUID. * We spawn 2 threads - one running the query and diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 8f7c3ea8acd4..c5e2ee4f00ff 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -155,6 +155,7 @@ import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; /** @@ -163,6 +164,12 @@ */ public class HiveConnection implements java.sql.Connection { private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class); + + /** + * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. + */ + static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; + private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; private String host; private int port; @@ -190,6 +197,21 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } + /** + * Records the effective {@code hive.query.timeout.seconds} (in seconds) after a successful + * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. + */ + void recordSessionQueryTimeoutFromSet(long seconds) { + sessionQueryTimeoutSeconds.set(seconds); + } + + /** + * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none + */ + long getSessionQueryTimeoutSecondsTracked() { + return sessionQueryTimeoutSeconds.get(); + } + /** * Get all direct HiveServer2 URLs from a ZooKeeper based HiveServer2 URL * @param zookeeperBasedHS2Url diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index aba982670acb..a8ea00e314e5 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -19,6 +19,7 @@ package org.apache.hive.jdbc; import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; import org.apache.hive.service.cli.RowSet; @@ -57,6 +58,9 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.apache.hadoop.hive.ql.ErrorMsg.CLIENT_POLLING_OPSTATUS_INTERRUPTED; @@ -70,6 +74,10 @@ public class HiveStatement implements java.sql.Statement { public static final String QUERY_CANCELLED_MESSAGE = "Query was cancelled."; + /** Last assignment wins if multiple appear (e.g. multi-line script). Uses find(), not full-string match. */ + private static final Pattern SET_HIVE_QUERY_TIMEOUT_SECONDS = Pattern.compile( + "(?i)set\\s+hive\\.query\\.timeout\\.seconds\\s*=\\s*([^;\\n]+)"); + private final HiveConnection connection; private TCLIService.Iface client; private Optional stmtHandle; @@ -298,6 +306,7 @@ public void closeOnCompletion() throws SQLException { public boolean execute(String sql) throws SQLException { runAsyncOnServer(sql); TGetOperationStatusResp status = waitForOperationToComplete(); + trackSessionQueryTimeoutIfSet(sql); // The query should be completed by now if (!status.isHasResultSet() && stmtHandle.isPresent() && !stmtHandle.get().isHasResultSet()) { @@ -398,6 +407,32 @@ private TGetOperationStatusResp waitForResultSetStatus() throws SQLException { return statusResp; } + /** + * When {@code SET hive.query.timeout.seconds=...} succeeds, remember the effective value on the + * connection so {@code TIMEDOUT_STATE} can report it if the server omits {@code errorMessage} + * (HIVE-28265). + */ + private void trackSessionQueryTimeoutIfSet(String sql) { + if (sql == null) { + return; + } + Matcher m = SET_HIVE_QUERY_TIMEOUT_SECONDS.matcher(sql); + Long lastSec = null; + while (m.find()) { + try { + HiveConf conf = new HiveConf(); + conf.set(HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, m.group(1).trim()); + long sec = HiveConf.getTimeVar(conf, HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + lastSec = sec; + } catch (Exception e) { + LOG.debug("Could not parse session query timeout fragment: {}", m.group(0), e); + } + } + if (lastSec != null) { + connection.recordSessionQueryTimeoutFromSet(lastSec); + } + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; @@ -441,8 +476,22 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { final String fullErrMsg = (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; throw new SQLException(fullErrMsg, "01000"); - case TIMEDOUT_STATE: - throw new SQLTimeoutException("Query timed out after " + queryTimeout + " seconds"); + case TIMEDOUT_STATE: { + String timeoutMsg = statusResp.getErrorMessage(); + // HIVE-28265: ignore blank or known-broken "0 seconds" from mismatched/old peers; rebuild + // from JDBC timeout or last SET hive.query.timeout.seconds on this connection. + boolean needLocalMessage = StringUtils.isBlank(timeoutMsg) + || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); + if (needLocalMessage) { + long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + long effectiveSec = queryTimeout > 0 ? queryTimeout + : (tracked > 0 ? tracked : 0); + timeoutMsg = effectiveSec > 0 + ? "Query timed out after " + effectiveSec + " seconds" + : "Query timed out"; + } + throw new SQLTimeoutException(timeoutMsg); + } case ERROR_STATE: // Get the error details from the underlying exception throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), diff --git a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java index de3e68b30c7d..8505e8d4a59e 100644 --- a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java +++ b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java @@ -179,6 +179,11 @@ private void prepare(QueryState queryState) throws HiveSQLException { try { final String queryId = queryState.getQueryId(); log.info("Query timed out after: {} seconds. Cancelling the execution now: {}", queryTimeout, queryId); + setOperationException(new HiveSQLException( + "Query timed out after " + queryTimeout + " seconds", + "HYT00", + 0, + queryId)); SQLOperation.this.cancel(OperationState.TIMEDOUT); } catch (HiveSQLException e) { log.error("Error cancelling the query after timeout: {} seconds", queryTimeout, e); @@ -334,7 +339,9 @@ public Object run() throws HiveSQLException { runQuery(); } catch (HiveSQLException e) { // TODO: why do we invent our own error path op top of the one from Future.get? - setOperationException(e); + if (getState() != OperationState.TIMEDOUT) { + setOperationException(e); + } log.error("Error running hive query", e); } finally { if (!embedded) { @@ -353,7 +360,9 @@ public Object run() throws HiveSQLException { try { currentUGI.doAs(doAsAction); } catch (Exception e) { - setOperationException(new HiveSQLException(e)); + if (getState() != OperationState.TIMEDOUT) { + setOperationException(new HiveSQLException(e)); + } log.error("Error running hive query as user : {}", currentUGI.getShortUserName(), e); } finally { /** From ab425d38c21727179d22c60e00e717048d737ad2 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 16:28:25 +0530 Subject: [PATCH 02/16] HIVE-28265: Refactor HiveStatement timeout handling; reset session timeout in tests - Extract TIMEDOUT/CANCELED handling into helpers to satisfy Sonar (complexity, nested blocks, nested ternary). - Add @After in TestJdbcDriver2 to run SET hive.query.timeout.seconds=0s on the shared connection so testQueryTimeoutMessageUsesHiveConf does not leave a 1s server-side limit for other tests (fixes Jenkins SQLTimeoutException cascades). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 20 ++++++ .../org/apache/hive/jdbc/HiveStatement.java | 62 ++++++++++++------- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 85a477632288..7102422fc922 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -42,6 +42,7 @@ import org.apache.hive.service.cli.operation.ClassicTableTypeMapping.ClassicTableTypes; import org.apache.hive.service.cli.operation.HiveTableTypeMapping; import org.apache.hive.service.cli.operation.TableTypeMappingFactory.TableTypeMappings; +import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -131,6 +132,25 @@ public class TestJdbcDriver2 { @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public final TestName testName = new TestName(); + /** + * {@code SET hive.query.timeout.seconds} applies to the whole HS2 session. Tests such as + * {@link #testQueryTimeoutMessageUsesHiveConf()} must not leave a short limit on the shared + * {@link #con}, or unrelated tests will see {@link SQLTimeoutException}. + */ + @After + public void resetHiveSessionQueryTimeout() { + try { + if (con == null || con.isClosed()) { + return; + } + try (Statement st = con.createStatement()) { + st.execute("set hive.query.timeout.seconds=0s"); + } + } catch (SQLException e) { + LOG.warn("Could not reset hive.query.timeout.seconds after {}", testName.getMethodName(), e); + } + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index a8ea00e314e5..7b81c2bd2885 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -433,6 +433,44 @@ private void trackSessionQueryTimeoutIfSet(String sql) { } } + /** + * HIVE-28265: Prefer server error text unless it is empty or the known-broken "0 seconds" case; + * otherwise derive seconds from JDBC {@link #setQueryTimeout(int)} or last session SET. + */ + private String sqlTimeoutMessageForTimedOutState(String serverMessage) { + if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { + return serverMessage; + } + long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); + if (effectiveSec > 0) { + return "Query timed out after " + effectiveSec + " seconds"; + } + return "Query timed out"; + } + + private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { + return StringUtils.isBlank(timeoutMsg) + || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); + } + + private long resolveEffectiveTimeoutSecondsForMessage() { + if (queryTimeout > 0) { + return queryTimeout; + } + long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + if (tracked > 0) { + return tracked; + } + return 0L; + } + + private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp statusResp) { + final String errMsg = statusResp.getErrorMessage(); + final String fullErrMsg = + (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; + return new SQLException(fullErrMsg, "01000"); + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; @@ -471,27 +509,9 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { isLogBeingGenerated = false; break; case CANCELED_STATE: - // 01000 -> warning - final String errMsg = statusResp.getErrorMessage(); - final String fullErrMsg = - (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; - throw new SQLException(fullErrMsg, "01000"); - case TIMEDOUT_STATE: { - String timeoutMsg = statusResp.getErrorMessage(); - // HIVE-28265: ignore blank or known-broken "0 seconds" from mismatched/old peers; rebuild - // from JDBC timeout or last SET hive.query.timeout.seconds on this connection. - boolean needLocalMessage = StringUtils.isBlank(timeoutMsg) - || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); - if (needLocalMessage) { - long tracked = connection.getSessionQueryTimeoutSecondsTracked(); - long effectiveSec = queryTimeout > 0 ? queryTimeout - : (tracked > 0 ? tracked : 0); - timeoutMsg = effectiveSec > 0 - ? "Query timed out after " + effectiveSec + " seconds" - : "Query timed out"; - } - throw new SQLTimeoutException(timeoutMsg); - } + throw sqlExceptionForCanceledState(statusResp); + case TIMEDOUT_STATE: + throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); case ERROR_STATE: // Get the error details from the underlying exception throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), From c5399f3812a5a553da15867c5de6479fe26e4555 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 13:09:54 +0530 Subject: [PATCH 03/16] HIVE-28265: Tighten query timeout message assertions in TestJdbcDriver2 Use a regex for 'timed out after 1 seconds' instead of contains("1") to avoid false positives (e.g. 10-second timeouts). Made-with: Cursor --- .../java/org/apache/hive/jdbc/TestJdbcDriver2.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 7102422fc922..183ba661f1bf 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -151,6 +151,14 @@ public void resetHiveSessionQueryTimeout() { } } + /** + * HS2 / {@code HiveStatement} report timeouts as {@code ...timed out after N seconds...}; match + * {@code N == 1} with flexible whitespace so we do not treat {@code 10} or unrelated digits as {@code 1}. + */ + private static boolean isQueryTimedOutAfterOneSecondMessage(String msg) { + return msg != null && msg.matches("(?is).*timed out after\\s+1\\s+seconds.*"); + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2683,7 +2691,7 @@ public void testQueryTimeout() throws Exception { } catch (SQLTimeoutException e) { assertNotNull(e); assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), - e.getMessage().contains("1")); + isQueryTimedOutAfterOneSecondMessage(e.getMessage())); assertFalse("Message should not claim 0 seconds: " + e.getMessage(), e.getMessage().contains("after 0 seconds")); System.err.println(e.toString()); @@ -2726,7 +2734,7 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { } catch (SQLTimeoutException e) { assertNotNull(e); assertTrue("Message should include session timeout (1s): " + e.getMessage(), - e.getMessage().contains("1")); + isQueryTimedOutAfterOneSecondMessage(e.getMessage())); assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), e.getMessage().contains("after 0 seconds")); } catch (SQLException e) { From eaae1325ef0e1bd3a370460269a4841b3938cd09 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 16:56:19 +0530 Subject: [PATCH 04/16] HIVE-28265: Address JDBC PR review (assertEquals, setter names, docs) - TestJdbcDriver2: assert exact 'Query timed out after 1 seconds'; drop redundant assertNotNull/assertFalse; keep HIVE-28265 context in Javadoc not assert text - HiveConnection: AtomicLong Javadoc for concurrent statements; rename to setSessionQueryTimeoutSeconds / getSessionQueryTimeoutSeconds - HiveStatement: update call sites Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 42 ++++++++++--------- .../org/apache/hive/jdbc/HiveConnection.java | 12 ++++-- .../org/apache/hive/jdbc/HiveStatement.java | 4 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 183ba661f1bf..57fe52c6e22d 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -129,6 +129,13 @@ public class TestJdbcDriver2 { private static Connection con; private static final float floatCompareDelta = 0.0001f; + /** + * Exact {@link SQLTimeoutException#getMessage()} for a 1s limit from HS2 / client; see + * {@code SQLOperation} and {@code HiveStatement#sqlTimeoutMessageForTimedOutState} (no query id + * or timestamp is appended to this text). + */ + private static final String QUERY_TIMED_OUT_AFTER_1_SECONDS = "Query timed out after 1 seconds"; + @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public final TestName testName = new TestName(); @@ -151,14 +158,6 @@ public void resetHiveSessionQueryTimeout() { } } - /** - * HS2 / {@code HiveStatement} report timeouts as {@code ...timed out after N seconds...}; match - * {@code N == 1} with flexible whitespace so we do not treat {@code 10} or unrelated digits as {@code 1}. - */ - private static boolean isQueryTimedOutAfterOneSecondMessage(String msg) { - return msg != null && msg.matches("(?is).*timed out after\\s+1\\s+seconds.*"); - } - private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2689,11 +2688,11 @@ public void testQueryTimeout() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertNotNull(e); - assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), - isQueryTimedOutAfterOneSecondMessage(e.getMessage())); - assertFalse("Message should not claim 0 seconds: " + e.getMessage(), - e.getMessage().contains("after 0 seconds")); + assertEquals( + "JDBC query timeout (1s) should match HS2/HiveStatement text, e.g. " + + QUERY_TIMED_OUT_AFTER_1_SECONDS, + QUERY_TIMED_OUT_AFTER_1_SECONDS, + e.getMessage()); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2713,8 +2712,11 @@ public void testQueryTimeout() throws Exception { } /** - * HIVE-28265: hive.query.timeout.seconds drives the server-side timer, but the JDBC client - * must not report "0 seconds" when Statement#setQueryTimeout was not used. + * When only {@code hive.query.timeout.seconds} applies (no {@link Statement#setQueryTimeout(int)}), + * the client must still report the real limit in {@link SQLTimeoutException#getMessage()} (before + * HIVE-28265 some paths wrongly showed "after 0 seconds"). Expected full message: + * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS} — same string as HS2 uses, with no query id or host + * suffix. */ @Test public void testQueryTimeoutMessageUsesHiveConf() throws Exception { @@ -2732,11 +2734,11 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertNotNull(e); - assertTrue("Message should include session timeout (1s): " + e.getMessage(), - isQueryTimedOutAfterOneSecondMessage(e.getMessage())); - assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), - e.getMessage().contains("after 0 seconds")); + assertEquals( + "Session query timeout (1s) should match HS2/HiveStatement text, e.g. " + + QUERY_TIMED_OUT_AFTER_1_SECONDS, + QUERY_TIMED_OUT_AFTER_1_SECONDS, + e.getMessage()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index c5e2ee4f00ff..28000675c37a 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -169,6 +169,12 @@ public class HiveConnection implements java.sql.Connection { * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. */ static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; + /** + * Last effective {@code hive.query.timeout.seconds} from a client {@code SET} (seconds), or + * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED}. A JDBC {@code Connection} may be shared across threads + * with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session; this field uses an + * {@link AtomicLong} so updates remain well-defined (last SET wins). + */ private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; private String host; @@ -198,17 +204,17 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } /** - * Records the effective {@code hive.query.timeout.seconds} (in seconds) after a successful + * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after a successful * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. */ - void recordSessionQueryTimeoutFromSet(long seconds) { + void setSessionQueryTimeoutSeconds(long seconds) { sessionQueryTimeoutSeconds.set(seconds); } /** * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none */ - long getSessionQueryTimeoutSecondsTracked() { + long getSessionQueryTimeoutSeconds() { return sessionQueryTimeoutSeconds.get(); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 7b81c2bd2885..af64ec495b83 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -429,7 +429,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { } } if (lastSec != null) { - connection.recordSessionQueryTimeoutFromSet(lastSec); + connection.setSessionQueryTimeoutSeconds(lastSec); } } @@ -457,7 +457,7 @@ private long resolveEffectiveTimeoutSecondsForMessage() { if (queryTimeout > 0) { return queryTimeout; } - long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + long tracked = connection.getSessionQueryTimeoutSeconds(); if (tracked > 0) { return tracked; } From aeb5a7cb1459779e586fa2b0ff75a2df260ce5cd Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 21:31:45 +0530 Subject: [PATCH 05/16] HIVE-28265: Strip Query ID suffix from JDBC SQLTimeoutException message HiveSQLException appends '; Query ID: ...' to getMessage(). When the client passes through the server timeout text (non-broken path), SQLTimeoutException included that suffix and TestJdbcDriver2 exact assertions failed on CI. Strip the same trailer the server uses before returning the message. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveStatement.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index af64ec495b83..8065e017cf5b 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -22,6 +22,7 @@ import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; +import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.RowSet; import org.apache.hive.service.cli.RowSetFactory; import org.apache.hive.service.rpc.thrift.TCLIService; @@ -439,7 +440,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { - return serverMessage; + return stripTrailingQueryIdSuffix(serverMessage); } long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); if (effectiveSec > 0) { @@ -448,6 +449,20 @@ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { return "Query timed out"; } + /** + * HS2 wraps many errors via {@link HiveSQLException#getMessage()}, which appends + * {@code ; Query ID: ...}. Strip that trailer so JDBC timeout text stays stable and matches the + * base HS2 wording (HIVE-28265). + */ + private static String stripTrailingQueryIdSuffix(String msg) { + if (msg == null) { + return null; + } + String marker = "; " + HiveSQLException.QUERY_ID + ": "; + int idx = msg.indexOf(marker); + return idx >= 0 ? msg.substring(0, idx) : msg; + } + private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { return StringUtils.isBlank(timeoutMsg) || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); From 6c5d8e9d73eb618b5a32d342ab9feb73f8d42e0b Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 14:43:09 +0530 Subject: [PATCH 06/16] HIVE-28265: Keep full HS2 timeout message; relax JDBC tests for Query ID suffix - HiveStatement: stop stripping '; Query ID;' from server timeout text; pass through when server message is usable (reverts strip-only follow-up). - TestJdbcDriver2: assert message starts with expected timeout text and does not contain 'after 0 seconds' (HIVE-28265), allowing HS2/HiveSQLException suffix. Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 36 ++++++++++--------- .../org/apache/hive/jdbc/HiveStatement.java | 17 +-------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 57fe52c6e22d..749be4ffc713 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -130,9 +130,8 @@ public class TestJdbcDriver2 { private static final float floatCompareDelta = 0.0001f; /** - * Exact {@link SQLTimeoutException#getMessage()} for a 1s limit from HS2 / client; see - * {@code SQLOperation} and {@code HiveStatement#sqlTimeoutMessageForTimedOutState} (no query id - * or timestamp is appended to this text). + * Required prefix of {@link SQLTimeoutException#getMessage()} for a 1s limit. HS2 may append + * {@code ; Query ID: ...} after the base text from {@code HiveSQLException}. */ private static final String QUERY_TIMED_OUT_AFTER_1_SECONDS = "Query timed out after 1 seconds"; @@ -158,6 +157,18 @@ public void resetHiveSessionQueryTimeout() { } } + private static void assertTimeoutMessageShowsOneSecond(String context, SQLTimeoutException e) { + String msg = e.getMessage(); + assertNotNull(context + ": message should not be null", msg); + assertTrue( + context + ": should start with " + QUERY_TIMED_OUT_AFTER_1_SECONDS + + " (HS2 may append ; Query ID: ...); actual=" + msg, + msg.startsWith(QUERY_TIMED_OUT_AFTER_1_SECONDS)); + assertFalse( + "HIVE-28265: message should not claim 0 seconds: " + msg, + msg.contains("after 0 seconds")); + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2688,11 +2699,8 @@ public void testQueryTimeout() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertEquals( - "JDBC query timeout (1s) should match HS2/HiveStatement text, e.g. " - + QUERY_TIMED_OUT_AFTER_1_SECONDS, - QUERY_TIMED_OUT_AFTER_1_SECONDS, - e.getMessage()); + assertTimeoutMessageShowsOneSecond( + "JDBC query timeout (1s)", e); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2714,9 +2722,8 @@ public void testQueryTimeout() throws Exception { /** * When only {@code hive.query.timeout.seconds} applies (no {@link Statement#setQueryTimeout(int)}), * the client must still report the real limit in {@link SQLTimeoutException#getMessage()} (before - * HIVE-28265 some paths wrongly showed "after 0 seconds"). Expected full message: - * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS} — same string as HS2 uses, with no query id or host - * suffix. + * HIVE-28265 some paths wrongly showed "after 0 seconds"). Message must begin with + * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS}; HS2 may append {@code ; Query ID: ...}. */ @Test public void testQueryTimeoutMessageUsesHiveConf() throws Exception { @@ -2734,11 +2741,8 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertEquals( - "Session query timeout (1s) should match HS2/HiveStatement text, e.g. " - + QUERY_TIMED_OUT_AFTER_1_SECONDS, - QUERY_TIMED_OUT_AFTER_1_SECONDS, - e.getMessage()); + assertTimeoutMessageShowsOneSecond( + "Session query timeout (1s)", e); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 8065e017cf5b..af64ec495b83 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -22,7 +22,6 @@ import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; -import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.RowSet; import org.apache.hive.service.cli.RowSetFactory; import org.apache.hive.service.rpc.thrift.TCLIService; @@ -440,7 +439,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { - return stripTrailingQueryIdSuffix(serverMessage); + return serverMessage; } long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); if (effectiveSec > 0) { @@ -449,20 +448,6 @@ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { return "Query timed out"; } - /** - * HS2 wraps many errors via {@link HiveSQLException#getMessage()}, which appends - * {@code ; Query ID: ...}. Strip that trailer so JDBC timeout text stays stable and matches the - * base HS2 wording (HIVE-28265). - */ - private static String stripTrailingQueryIdSuffix(String msg) { - if (msg == null) { - return null; - } - String marker = "; " + HiveSQLException.QUERY_ID + ": "; - int idx = msg.indexOf(marker); - return idx >= 0 ? msg.substring(0, idx) : msg; - } - private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { return StringUtils.isBlank(timeoutMsg) || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); From ac0063cabf450fb3c5cf29d2b8df6ac37bd582f4 Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 17:54:54 +0530 Subject: [PATCH 07/16] HIVE-28265: Refactor HiveStatement for Sonar (not all Sonar PR issues) - sqlExceptionForCanceledState: replace ternary with if/else (S3358). - Extract processOperationStatusResponse from waitForOperationToComplete (S6541). - Use local progressUpdates flag to simplify nested flow (AvoidNestedBlocks). Does not address hundreds of repo-wide Sonar findings outside this file. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveStatement.java | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index af64ec495b83..2ab21be0be87 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -466,25 +466,61 @@ private long resolveEffectiveTimeoutSecondsForMessage() { private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp statusResp) { final String errMsg = statusResp.getErrorMessage(); - final String fullErrMsg = - (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; + final String fullErrMsg; + if (errMsg == null || errMsg.isEmpty()) { + fullErrMsg = QUERY_CANCELLED_MESSAGE; + } else { + fullErrMsg = QUERY_CANCELLED_MESSAGE + " " + errMsg; + } return new SQLException(fullErrMsg, "01000"); } + /** + * One GetOperationStatus response: progress update, Thrift status check, then terminal states. + * Extracted to keep {@link #waitForOperationToComplete()} smaller for static analysis (Sonar). + */ + private void processOperationStatusResponse(TGetOperationStatusResp statusResp) throws SQLException { + if (!isOperationComplete && inPlaceUpdateStream.isPresent()) { + inPlaceUpdateStream.get().update(statusResp.getProgressUpdateResponse()); + } + Utils.verifySuccessWithInfo(statusResp.getStatus()); + if (!statusResp.isSetOperationState()) { + return; + } + switch (statusResp.getOperationState()) { + case CLOSED_STATE: + case FINISHED_STATE: + isOperationComplete = true; + isLogBeingGenerated = false; + break; + case CANCELED_STATE: + throw sqlExceptionForCanceledState(statusResp); + case TIMEDOUT_STATE: + throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); + case ERROR_STATE: + throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), statusResp.getErrorCode()); + case UKNOWN_STATE: + throw new SQLException("Unknown query", "HY000"); + case INITIALIZED_STATE: + case PENDING_STATE: + case RUNNING_STATE: + break; + } + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; final TGetOperationStatusReq statusReq = new TGetOperationStatusReq(stmtHandle.get()); - statusReq.setGetProgressUpdate(inPlaceUpdateStream.isPresent()); + boolean progressUpdates = inPlaceUpdateStream.isPresent(); + statusReq.setGetProgressUpdate(progressUpdates); - // Progress bar is completed if there is nothing to request - if (inPlaceUpdateStream.isPresent()) { + if (progressUpdates) { inPlaceUpdateStream.get().getEventNotifier().progressBarCompleted(); } LOG.debug("Waiting on operation to complete: Polling operation status"); - // Poll on the operation status, till the operation is complete do { try { if (Thread.currentThread().isInterrupted()) { @@ -497,33 +533,7 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { */ statusResp = client.GetOperationStatus(statusReq); LOG.debug("Status response: {}", statusResp); - if (!isOperationComplete && inPlaceUpdateStream.isPresent()) { - inPlaceUpdateStream.get().update(statusResp.getProgressUpdateResponse()); - } - Utils.verifySuccessWithInfo(statusResp.getStatus()); - if (statusResp.isSetOperationState()) { - switch (statusResp.getOperationState()) { - case CLOSED_STATE: - case FINISHED_STATE: - isOperationComplete = true; - isLogBeingGenerated = false; - break; - case CANCELED_STATE: - throw sqlExceptionForCanceledState(statusResp); - case TIMEDOUT_STATE: - throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); - case ERROR_STATE: - // Get the error details from the underlying exception - throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), - statusResp.getErrorCode()); - case UKNOWN_STATE: - throw new SQLException("Unknown query", "HY000"); - case INITIALIZED_STATE: - case PENDING_STATE: - case RUNNING_STATE: - break; - } - } + processOperationStatusResponse(statusResp); } catch (SQLException e) { isLogBeingGenerated = false; throw e; @@ -533,8 +543,7 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { } } while (!isOperationComplete); - // set progress bar to be completed when hive query execution has completed - if (inPlaceUpdateStream.isPresent()) { + if (progressUpdates) { inPlaceUpdateStream.get().getEventNotifier().progressBarCompleted(); } return statusResp; From 9449a43ccef4a53c0fb86eccd49f87d590f89b70 Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 23:04:58 +0530 Subject: [PATCH 08/16] HIVE-28265: Fix flaky TestPartitionManagement concurrent skip counter Remove skippedAttempts reset on lock acquisition that could wipe concurrent increments; use AtomicInteger for skipped attempts; reset test counters in @Before to isolate tests. Made-with: Cursor --- .../metastore/PartitionManagementTask.java | 18 +++++++++++++----- .../metastore/TestPartitionManagement.java | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index fa9d5e2e9dd6..de18f1c3da1c 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -59,7 +60,8 @@ public class PartitionManagementTask implements MetastoreTaskThread { private static final Lock lock = new ReentrantLock(); // these are just for testing private static int completedAttempts; - private static int skippedAttempts; + /** Atomic: concurrent run() threads may take the tryLock() failure path together. */ + private static final AtomicInteger skippedAttempts = new AtomicInteger(0); private Configuration conf; @@ -87,7 +89,6 @@ private static boolean partitionDiscoveryEnabled(Map params) { @Override public void run() { if (lock.tryLock()) { - skippedAttempts = 0; String qualifiedTableName = null; IMetaStoreClient msc = null; try { @@ -138,8 +139,8 @@ public void run() { } completedAttempts++; } else { - skippedAttempts++; - LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skippedAttempts); + int skipped = skippedAttempts.incrementAndGet(); + LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skipped); } } @@ -202,7 +203,14 @@ public void run() { @VisibleForTesting public static int getSkippedAttempts() { - return skippedAttempts; + return skippedAttempts.get(); + } + + /** Reset counters between tests; not for production use. */ + @VisibleForTesting + static void resetCountersForTesting() { + completedAttempts = 0; + skippedAttempts.set(0); } @VisibleForTesting diff --git a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java index e2fd7bf9cc51..ebfc98419f01 100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java @@ -81,6 +81,7 @@ public void setUp() throws Exception { TestTxnDbUtil.setConfValues(conf); TestTxnDbUtil.prepDb(conf); client = new HiveMetaStoreClient(conf); + PartitionManagementTask.resetCountersForTesting(); } @After From 2ca54afd643788c4a842c2dac250c834255718b8 Mon Sep 17 00:00:00 2001 From: akpatra Date: Sat, 11 Apr 2026 09:24:18 +0530 Subject: [PATCH 09/16] HIVE-28265: Remove unused partitionDiscoveryEnabled (Sonar) Made-with: Cursor --- .../hadoop/hive/metastore/PartitionManagementTask.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index de18f1c3da1c..20f0e20efd19 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,12 +19,11 @@ package org.apache.hadoop.hive.metastore; import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -81,11 +80,6 @@ public Configuration getConf() { return conf; } - private static boolean partitionDiscoveryEnabled(Map params) { - return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && - params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); - } - @Override public void run() { if (lock.tryLock()) { From 32aafd20d0e7d7b3408ad4443d6d9e1cb314ad74 Mon Sep 17 00:00:00 2001 From: akpatra Date: Sat, 11 Apr 2026 12:58:16 +0530 Subject: [PATCH 10/16] Revert "HIVE-28265: Remove unused partitionDiscoveryEnabled (Sonar)" This reverts commit 2ca54afd643788c4a842c2dac250c834255718b8. --- .../hadoop/hive/metastore/PartitionManagementTask.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index 20f0e20efd19..de18f1c3da1c 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,11 +19,12 @@ package org.apache.hadoop.hive.metastore; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -80,6 +81,11 @@ public Configuration getConf() { return conf; } + private static boolean partitionDiscoveryEnabled(Map params) { + return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && + params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); + } + @Override public void run() { if (lock.tryLock()) { From 8283cd85dcdba66c95f37fc1ed4b214952380b0c Mon Sep 17 00:00:00 2001 From: akpatra Date: Sat, 11 Apr 2026 12:58:57 +0530 Subject: [PATCH 11/16] HIVE-28265: Checkstyle SKIPPED_ATTEMPTS; fix Beeline/llap precommit tests - Rename AtomicInteger counter to SKIPPED_ATTEMPTS (ConstantNameCheck). - Drop unused partitionDiscoveryEnabled again (revert commit restores history). - testQueryProgress: accept ELAPSED TIME, Beeline row timing, or Driver Time taken. - llap_io_cache: use 8MiB RPAD payload to avoid Parquet logging OOM on CI. Made-with: Cursor --- .../apache/hive/beeline/TestBeeLineWithArgs.java | 5 +++-- .../test/queries/clientpositive/llap_io_cache.q | 2 +- .../clientpositive/llap/llap_io_cache.q.out | 8 ++++---- .../hive/metastore/PartitionManagementTask.java | 16 +++++----------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java index ffb9ab1224af..3a61ae5c4ef9 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java @@ -789,8 +789,9 @@ public void testQueryProgress() throws Throwable { "set hive.support.concurrency = false;\n" + "set hive.server2.logging.operation.level=execution;\n" + "select count(*) from " + tableName + ";\n"; - // Check for part of log message as well as part of progress information - final String EXPECTED_PATTERN = "ELAPSED TIME"; + // HS2 may log "ELAPSED TIME" (legacy); Beeline may print row timing or Driver logs "Time taken:" on stderr. + final String EXPECTED_PATTERN = + "(ELAPSED TIME|row selected \\([0-9]+\\.[0-9]+ seconds\\)|Time taken:)"; final String UNEXPECTED_PATTERN = "(?=Reducer 2\\:).*(?=Map 1\\:)"; testScriptFile(SCRIPT_TEXT, getBaseArgs(miniHS2.getBaseJdbcURL()), OutStream.ERR, Arrays.asList( diff --git a/ql/src/test/queries/clientpositive/llap_io_cache.q b/ql/src/test/queries/clientpositive/llap_io_cache.q index b5ab5b25bae9..170fddc7008d 100644 --- a/ql/src/test/queries/clientpositive/llap_io_cache.q +++ b/ql/src/test/queries/clientpositive/llap_io_cache.q @@ -19,7 +19,7 @@ TBLPROPERTIES ( INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload; + RPAD('x', 8388608, 'x') AS payload; SELECT LENGTH(payload) FROM tbl_parq; diff --git a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out index 765ae2216310..7e264869f9d8 100644 --- a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out +++ b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out @@ -33,14 +33,14 @@ POSTHOOK: Output: default@tbl_parq PREHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload + RPAD('x', 8388608, 'x') AS payload PREHOOK: type: QUERY PREHOOK: Input: _dummy_database@_dummy_table PREHOOK: Output: default@tbl_parq POSTHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload + RPAD('x', 8388608, 'x') AS payload POSTHOOK: type: QUERY POSTHOOK: Input: _dummy_database@_dummy_table POSTHOOK: Output: default@tbl_parq @@ -54,7 +54,7 @@ POSTHOOK: query: SELECT LENGTH(payload) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -16777177 +8388608 PREHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq PREHOOK: type: QUERY PREHOOK: Input: default@tbl_parq @@ -63,4 +63,4 @@ POSTHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -16777177 +8388608 diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index de18f1c3da1c..2f8aac00e4f5 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,12 +19,11 @@ package org.apache.hadoop.hive.metastore; import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -61,7 +60,7 @@ public class PartitionManagementTask implements MetastoreTaskThread { // these are just for testing private static int completedAttempts; /** Atomic: concurrent run() threads may take the tryLock() failure path together. */ - private static final AtomicInteger skippedAttempts = new AtomicInteger(0); + private static final AtomicInteger SKIPPED_ATTEMPTS = new AtomicInteger(0); private Configuration conf; @@ -81,11 +80,6 @@ public Configuration getConf() { return conf; } - private static boolean partitionDiscoveryEnabled(Map params) { - return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && - params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); - } - @Override public void run() { if (lock.tryLock()) { @@ -139,7 +133,7 @@ public void run() { } completedAttempts++; } else { - int skipped = skippedAttempts.incrementAndGet(); + int skipped = SKIPPED_ATTEMPTS.incrementAndGet(); LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skipped); } } @@ -203,14 +197,14 @@ public void run() { @VisibleForTesting public static int getSkippedAttempts() { - return skippedAttempts.get(); + return SKIPPED_ATTEMPTS.get(); } /** Reset counters between tests; not for production use. */ @VisibleForTesting static void resetCountersForTesting() { completedAttempts = 0; - skippedAttempts.set(0); + SKIPPED_ATTEMPTS.set(0); } @VisibleForTesting From d7176380b62b175925ce5d8e84201b079e2f8c6c Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 10:09:32 +0530 Subject: [PATCH 12/16] HIVE-28265: Remove non-core files from PR (match master) Restore Beeline/llap_io_cache Q test, PartitionManagementTask, and TestPartitionManagement to upstream master. Core fix remains: SQLOperation, HiveStatement, HiveConnection, TestJdbcDriver2. Made-with: Cursor --- .../hive/beeline/TestBeeLineWithArgs.java | 5 ++-- .../queries/clientpositive/llap_io_cache.q | 2 +- .../clientpositive/llap/llap_io_cache.q.out | 8 +++---- .../metastore/PartitionManagementTask.java | 24 +++++++++---------- .../metastore/TestPartitionManagement.java | 1 - 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java index 3a61ae5c4ef9..ffb9ab1224af 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java @@ -789,9 +789,8 @@ public void testQueryProgress() throws Throwable { "set hive.support.concurrency = false;\n" + "set hive.server2.logging.operation.level=execution;\n" + "select count(*) from " + tableName + ";\n"; - // HS2 may log "ELAPSED TIME" (legacy); Beeline may print row timing or Driver logs "Time taken:" on stderr. - final String EXPECTED_PATTERN = - "(ELAPSED TIME|row selected \\([0-9]+\\.[0-9]+ seconds\\)|Time taken:)"; + // Check for part of log message as well as part of progress information + final String EXPECTED_PATTERN = "ELAPSED TIME"; final String UNEXPECTED_PATTERN = "(?=Reducer 2\\:).*(?=Map 1\\:)"; testScriptFile(SCRIPT_TEXT, getBaseArgs(miniHS2.getBaseJdbcURL()), OutStream.ERR, Arrays.asList( diff --git a/ql/src/test/queries/clientpositive/llap_io_cache.q b/ql/src/test/queries/clientpositive/llap_io_cache.q index 170fddc7008d..b5ab5b25bae9 100644 --- a/ql/src/test/queries/clientpositive/llap_io_cache.q +++ b/ql/src/test/queries/clientpositive/llap_io_cache.q @@ -19,7 +19,7 @@ TBLPROPERTIES ( INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload; + RPAD('x', 16777177, 'x') AS payload; SELECT LENGTH(payload) FROM tbl_parq; diff --git a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out index 7e264869f9d8..765ae2216310 100644 --- a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out +++ b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out @@ -33,14 +33,14 @@ POSTHOOK: Output: default@tbl_parq PREHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload + RPAD('x', 16777177, 'x') AS payload PREHOOK: type: QUERY PREHOOK: Input: _dummy_database@_dummy_table PREHOOK: Output: default@tbl_parq POSTHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload + RPAD('x', 16777177, 'x') AS payload POSTHOOK: type: QUERY POSTHOOK: Input: _dummy_database@_dummy_table POSTHOOK: Output: default@tbl_parq @@ -54,7 +54,7 @@ POSTHOOK: query: SELECT LENGTH(payload) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -8388608 +16777177 PREHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq PREHOOK: type: QUERY PREHOOK: Input: default@tbl_parq @@ -63,4 +63,4 @@ POSTHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -8388608 +16777177 diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index 2f8aac00e4f5..fa9d5e2e9dd6 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,11 +19,11 @@ package org.apache.hadoop.hive.metastore; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -59,8 +59,7 @@ public class PartitionManagementTask implements MetastoreTaskThread { private static final Lock lock = new ReentrantLock(); // these are just for testing private static int completedAttempts; - /** Atomic: concurrent run() threads may take the tryLock() failure path together. */ - private static final AtomicInteger SKIPPED_ATTEMPTS = new AtomicInteger(0); + private static int skippedAttempts; private Configuration conf; @@ -80,9 +79,15 @@ public Configuration getConf() { return conf; } + private static boolean partitionDiscoveryEnabled(Map params) { + return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && + params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); + } + @Override public void run() { if (lock.tryLock()) { + skippedAttempts = 0; String qualifiedTableName = null; IMetaStoreClient msc = null; try { @@ -133,8 +138,8 @@ public void run() { } completedAttempts++; } else { - int skipped = SKIPPED_ATTEMPTS.incrementAndGet(); - LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skipped); + skippedAttempts++; + LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skippedAttempts); } } @@ -197,14 +202,7 @@ public void run() { @VisibleForTesting public static int getSkippedAttempts() { - return SKIPPED_ATTEMPTS.get(); - } - - /** Reset counters between tests; not for production use. */ - @VisibleForTesting - static void resetCountersForTesting() { - completedAttempts = 0; - SKIPPED_ATTEMPTS.set(0); + return skippedAttempts; } @VisibleForTesting diff --git a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java index ebfc98419f01..e2fd7bf9cc51 100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestPartitionManagement.java @@ -81,7 +81,6 @@ public void setUp() throws Exception { TestTxnDbUtil.setConfValues(conf); TestTxnDbUtil.prepDb(conf); client = new HiveMetaStoreClient(conf); - PartitionManagementTask.resetCountersForTesting(); } @After From 73eebcf70ce7ff10476202073802ff372ade5b43 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 20:49:03 +0530 Subject: [PATCH 13/16] HIVE-28265: Add JDBC URL query timeout test (like testURLWithFetchSize) testURLWithHiveQueryTimeoutSeconds sets hive.query.timeout.seconds via the URL query string (getConnection postfix), matching the driver doc for db;sess?hive_conf. Asserts timeout message shows 1s (HIVE-28265). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 749be4ffc713..c0a7219547ae 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -381,6 +381,41 @@ public void testURLWithFetchSize() throws SQLException { con.close(); } + /** + * Same idea as {@link #testURLWithFetchSize}: drive session behavior from the JDBC URL instead of + * only {@link Statement#setQueryTimeout(int)} or an explicit {@code SET}. The timeout is supplied + * in the URL query ({@code ?hive_conf_list}) per the driver format + * {@code jdbc:hive2://.../db;sess?hive_conf#hive_var}. + *

+ * HIVE-28265: {@link SQLTimeoutException#getMessage()} must reflect the configured limit (1s), + * not {@code after 0 seconds}. + */ + @Test + public void testURLWithHiveQueryTimeoutSeconds() throws Exception { + String udfName = SleepMsUDF.class.getName(); + // Postfix appends to the query string after test overlay / lock manager settings. + Connection con = getConnection(testDbName, "hive.query.timeout.seconds=1"); + try { + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + Statement stmt = con.createStatement(); + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertTimeoutMessageShowsOneSecond("JDBC URL hive.query.timeout.seconds=1 (query string)", e); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + } + stmt.close(); + } finally { + con.close(); + } + } + @Test /** * Test setting create external purge table by default in jdbc config From 5b110e6a6ddb0118f8e5c0ee09236507a06bad13 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 23:32:25 +0530 Subject: [PATCH 14/16] HIVE-28265: Rename local Connection vars to avoid hiding TestJdbcDriver2.con Sonar: local name 'con' shadowed static field 'con' (HiddenField). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index c0a7219547ae..18b7b8824138 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -374,11 +374,11 @@ private void checkBadUrl(String url) throws SQLException { * @throws SQLException */ public void testURLWithFetchSize() throws SQLException { - Connection con = getConnection(testDbName + ";fetchSize=1234", ""); - Statement stmt = con.createStatement(); + Connection connectionWithFetchSize = getConnection(testDbName + ";fetchSize=1234", ""); + Statement stmt = connectionWithFetchSize.createStatement(); assertEquals(stmt.getFetchSize(), 1234); stmt.close(); - con.close(); + connectionWithFetchSize.close(); } /** @@ -394,12 +394,12 @@ public void testURLWithFetchSize() throws SQLException { public void testURLWithHiveQueryTimeoutSeconds() throws Exception { String udfName = SleepMsUDF.class.getName(); // Postfix appends to the query string after test overlay / lock manager settings. - Connection con = getConnection(testDbName, "hive.query.timeout.seconds=1"); + Connection connectionWithUrlQueryTimeout = getConnection(testDbName, "hive.query.timeout.seconds=1"); try { - Statement stmt1 = con.createStatement(); + Statement stmt1 = connectionWithUrlQueryTimeout.createStatement(); stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); stmt1.close(); - Statement stmt = con.createStatement(); + Statement stmt = connectionWithUrlQueryTimeout.createStatement(); try { stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + "t2.under_col as u2 from " + tableName + " t1 join " + tableName @@ -412,7 +412,7 @@ public void testURLWithHiveQueryTimeoutSeconds() throws Exception { } stmt.close(); } finally { - con.close(); + connectionWithUrlQueryTimeout.close(); } } @@ -422,13 +422,13 @@ public void testURLWithHiveQueryTimeoutSeconds() throws Exception { * @throws SQLException */ public void testCreateTableAsExternal() throws SQLException { - Connection con = getConnection(testDbName + ";hiveCreateAsExternalLegacy=true", ""); - Statement stmt = con.createStatement(); + Connection connectionWithExternalLegacy = getConnection(testDbName + ";hiveCreateAsExternalLegacy=true", ""); + Statement stmt = connectionWithExternalLegacy.createStatement(); ResultSet res = stmt.executeQuery("set hive.create.as.external.legacy"); assertTrue("ResultSet is empty", res.next()); assertEquals("hive.create.as.external.legacy=true", res.getObject(1)); stmt.close(); - con.close(); + connectionWithExternalLegacy.close(); } @Test From d88a80cb9225ab8be71b60c2f0d8cf367db2d42f Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 21 Apr 2026 21:26:51 +0530 Subject: [PATCH 15/16] HIVE-28265: Seed session query timeout from JDBC URL hive conf Parse hive.query.timeout.seconds from connParams.getHiveConfs() at connect time using HiveConf.getTimeVar (same semantics as HiveStatement SET path) so JDBC timeout messages work when the timeout is set via URL only. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveConnection.java | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 28000675c37a..cc48ccb0b891 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -69,6 +69,7 @@ import java.util.Optional; import java.util.Properties; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Stream; @@ -82,6 +83,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hive.common.auth.HiveAuthUtils; +import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.conf.HiveConf.ConfVars; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -166,14 +168,17 @@ public class HiveConnection implements java.sql.Connection { private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class); /** - * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. + * Sentinel: no {@code hive.query.timeout.seconds} has been applied from the JDBC URL or a client + * {@code SET} on this connection yet. */ static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; /** - * Last effective {@code hive.query.timeout.seconds} from a client {@code SET} (seconds), or + * Last effective {@code hive.query.timeout.seconds} in seconds: from the JDBC URL + * ({@code ?hive.query.timeout.seconds=...} / {@code hiveconf:} map) at connect time, and/or from a + * client {@code SET} (see {@link org.apache.hive.jdbc.HiveStatement}), or * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED}. A JDBC {@code Connection} may be shared across threads * with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session; this field uses an - * {@link AtomicLong} so updates remain well-defined (last SET wins). + * {@link AtomicLong} so updates remain well-defined (URL first, then last SET wins over prior value). */ private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; @@ -204,15 +209,43 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } /** - * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after a successful - * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. + * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after connect (URL) or a + * successful {@code SET hive.query.timeout.seconds=...}. Used for JDBC timeout messages (HIVE-28265). */ void setSessionQueryTimeoutSeconds(long seconds) { sessionQueryTimeoutSeconds.set(seconds); } /** - * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none + * If the JDBC URL supplied {@code hive.query.timeout.seconds} (query string / {@code hiveconf:} map), + * parse and store it for {@link #getSessionQueryTimeoutSeconds()} so timeout error messages can use it + * without regex-parsing {@code SET} statements. Does not change HS2 behavior (already applied in + * {@link #openSession()}). + */ + private void applySessionQueryTimeoutFromJdbcUrl() { + Map hiveConfs = connParams.getHiveConfs(); + if (hiveConfs == null || hiveConfs.isEmpty()) { + return; + } + String raw = hiveConfs.get(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname); + if (StringUtils.isBlank(raw)) { + return; + } + try { + HiveConf conf = new HiveConf(); + conf.set(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw.trim()); + long sec = HiveConf.getTimeVar(conf, ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + if (sec > 0) { + setSessionQueryTimeoutSeconds(sec); + } + } catch (Exception e) { + LOG.debug("Could not parse {} from JDBC URL: {}", ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw, e); + } + } + + /** + * @return seconds from the JDBC URL at connect and/or the last client-tracked SET, or + * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if neither applied */ long getSessionQueryTimeoutSeconds() { return sessionQueryTimeoutSeconds.get(); @@ -360,6 +393,7 @@ protected HiveConnection(String uri, Properties info, // hive_conf_list -> hiveConfMap // hive_var_list -> hiveVarMap sessConfMap = connParams.getSessionVars(); + applySessionQueryTimeoutFromJdbcUrl(); setupLoginTimeout(); if (isKerberosAuthMode()) { // Ensure UserGroupInformation includes any authorized Kerberos principals. From 8017eefe3d3af36c3eb7b995be1f3ae440aa940b Mon Sep 17 00:00:00 2001 From: akpatra Date: Wed, 22 Apr 2026 07:26:57 +0530 Subject: [PATCH 16/16] HIVE-28265: Add test for session timeout message across multiple statements testQueryTimeoutMessagePersistedAcrossStatements verifies that when SET hive.query.timeout.seconds is issued on a separate closed statement, the tracked value on HiveConnection still drives the SQLTimeoutException message on a subsequent new statement (no setQueryTimeout call). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 18b7b8824138..c6cc018b2a9d 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -2784,6 +2784,52 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { stmt.close(); } + /** + * Variant of {@link #testQueryTimeoutMessageUsesHiveConf}: the {@code SET} is issued on a + * separate, already-closed statement; the timed-out query runs on a brand-new statement with no + * {@link Statement#setQueryTimeout(int)} call. The tracked session timeout lives on the + * {@link HiveConnection}, so it persists across statement instances and must still drive the + * {@link SQLTimeoutException} message correctly. Covers the HIVE-28265 multi-statement scenario. + */ + @Test + public void testQueryTimeoutMessagePersistedAcrossStatements() throws Exception { + String udfName = SleepMsUDF.class.getName(); + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + + // SET is issued on stmt2, which is then closed – timeout must survive on the connection + Statement stmt2 = con.createStatement(); + stmt2.execute("set hive.query.timeout.seconds=1s"); + stmt2.close(); + + // Brand-new statement, no setQueryTimeout() call – relies solely on the tracked session value + Statement stmt = con.createStatement(); + System.err.println("Executing query (expecting timeout): "); + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertTimeoutMessageShowsOneSecond("SET on closed stmt2, executeQuery on new stmt", e); + System.err.println(e.toString()); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + e.printStackTrace(); + } + + // A fast query must still complete when the per-statement timeout overrides + stmt.setQueryTimeout(5); + try { + stmt.executeQuery("show tables"); + } catch (SQLException e) { + fail("Unexpected SQLException: " + e); + e.printStackTrace(); + } + stmt.close(); + } + /** * Test the non-null value of the Yarn ATS GUID. * We spawn 2 threads - one running the query and